Codebase refactor #3
|
@ -17,6 +17,24 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.7"
|
version = "0.6.7"
|
||||||
|
@ -116,6 +134,21 @@ version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cassowary"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "castaway"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.83"
|
version = "1.0.83"
|
||||||
|
@ -162,7 +195,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -171,12 +204,52 @@ version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color-eyre"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"color-spantrace",
|
||||||
|
"eyre",
|
||||||
|
"indenter",
|
||||||
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
|
"tracing-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color-spantrace"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-error",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "compact_str"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"cfg-if",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -193,6 +266,31 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
|
@ -214,6 +312,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
|
@ -239,6 +343,16 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "eyre"
|
||||||
|
version = "0.6.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
||||||
|
dependencies = [
|
||||||
|
"indenter",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
@ -355,6 +469,10 @@ name = "hashbrown"
|
||||||
version = "0.14.2"
|
version = "0.14.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -449,6 +567,12 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indenter"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -459,12 +583,27 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -525,6 +664,15 @@ version = "0.4.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
|
@ -563,6 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
@ -633,7 +782,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -660,6 +809,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owo-colors"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -683,6 +838,12 @@ dependencies = [
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
@ -712,7 +873,10 @@ name = "pluralsync"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"color-eyre",
|
||||||
|
"crossterm",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"ratatui",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -737,6 +901,26 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui"
|
||||||
|
version = "0.26.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "154b85ef15a5d1719bcaa193c3c81fe645cd120c156874cd660fe49fd21d1373"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"cassowary",
|
||||||
|
"compact_str",
|
||||||
|
"crossterm",
|
||||||
|
"indoc",
|
||||||
|
"itertools",
|
||||||
|
"lru",
|
||||||
|
"paste",
|
||||||
|
"stability",
|
||||||
|
"strum",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -815,6 +999,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
|
@ -876,7 +1066,7 @@ checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -902,6 +1092,36 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
@ -946,12 +1166,61 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stability"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.39"
|
version = "2.0.39"
|
||||||
|
@ -1014,7 +1283,17 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1059,7 +1338,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1109,6 +1388,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-error"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"sharded-slab",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1147,6 +1448,18 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
@ -1164,6 +1477,12 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1212,7 +1531,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1246,7 +1565,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.39",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -1430,3 +1749,23 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.39",
|
||||||
|
]
|
||||||
|
|
|
@ -13,8 +13,12 @@ serde_json = "1.0"
|
||||||
reqwest = { version = "0.11.22", features = ["json", "multipart"] }
|
reqwest = { version = "0.11.22", features = ["json", "multipart"] }
|
||||||
clap = { version = "4.4.10", features = ["derive"] }
|
clap = { version = "4.4.10", features = ["derive"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
ratatui = "0.26.0"
|
||||||
|
crossterm = "0.27.0"
|
||||||
|
color-eyre = "0.6.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["avatar"]
|
||||||
avatar = []
|
avatar = []
|
||||||
discord = ["avatar"]
|
discord = ["avatar"]
|
||||||
fedi = ["avatar"]
|
fedi = ["avatar"]
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
pub mod pk {
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use reqwest::header::{USER_AGENT, AUTHORIZATION};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::api::request::*;
|
||||||
|
use crate::systemdata::*;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
|
||||||
|
pub const PK_URL: &str = "https://api.pluralkit.me/v2";
|
||||||
|
|
||||||
|
pub fn get_system(key: &str) -> Result<Value> {
|
||||||
|
let url = format!("{}/systems/@me", PK_URL);
|
||||||
|
|
||||||
|
let response = http_get(url,key)?;
|
||||||
|
|
||||||
|
Ok(serde_json::from_str(&response)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_members(key: &str, sysid: &str) -> Result<Vec<Value>> {
|
||||||
|
let url = format!("{}/systems/{}/members", PK_URL, sysid);
|
||||||
|
|
||||||
|
let response = http_get(url,key)?;
|
||||||
|
|
||||||
|
Ok(serde_json::from_str(&response)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fronters(key: &str, sys: &System) -> Result<Vec<Member>> {
|
||||||
|
let url = format!("{}/systems/{}/fronters", PK_URL, sys.pk_userid);
|
||||||
|
|
||||||
|
let result = http_get(url,key)?;
|
||||||
|
let json: Value = serde_json::from_str(&result)?;
|
||||||
|
let json_members = &json["members"].as_array();
|
||||||
|
|
||||||
|
let mut members: Vec<Member> = Vec::new();
|
||||||
|
for member in json_members {
|
||||||
|
for m in member.into_iter() {
|
||||||
|
for dbmem in &sys.members {
|
||||||
|
if m["name"].to_string() == dbmem.name {
|
||||||
|
members.push(dbmem.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn set_fronters(key: &str, sys: &System, to_front: &Vec<Member>, fronters: &Fronters) -> Result<()> {
|
||||||
|
let url = format!("{}/systems/{}/switches", PK_URL, sys.pk_userid);
|
||||||
|
|
||||||
|
if to_front != &fronters.pk {
|
||||||
|
let mut frontcodes = Vec::new();
|
||||||
|
for tf in to_front {
|
||||||
|
frontcodes.push(String::from(&tf.pk_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body: HashMap<&str, Vec<String>> = HashMap::new();
|
||||||
|
body.insert("members", frontcodes);
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let rb = client
|
||||||
|
.post(url)
|
||||||
|
.json(&body)
|
||||||
|
.header("content-type", "application/json; charset=utf-8")
|
||||||
|
.header(USER_AGENT, "Pluralsync")
|
||||||
|
.header(AUTHORIZATION, key);
|
||||||
|
|
||||||
|
match http_request(rb) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(eyre!("{}", e.to_string())),
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
println!("Members already fonting");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod sp {
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use reqwest::header::{USER_AGENT, AUTHORIZATION};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use crate::api::request::*;
|
||||||
|
use crate::systemdata::*;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
|
||||||
|
pub const SP_URL: &str = "https://api.apparyllis.com/v1";
|
||||||
|
|
||||||
|
pub fn get_user_id(key: &str) -> Result<String> {
|
||||||
|
let url = format!("{}/me", SP_URL);
|
||||||
|
|
||||||
|
let response = http_get(url, key)?;
|
||||||
|
|
||||||
|
let json: Value = serde_json::from_str(&response)?;
|
||||||
|
|
||||||
|
Ok(json["id"].to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_member_ids(key: &str, system_id: &str) -> Result<HashMap<String, String>> {
|
||||||
|
let url = format!("{}/members/{}", SP_URL, system_id);
|
||||||
|
|
||||||
|
let response = http_get(url,key)?;
|
||||||
|
let json: Vec<Value> = serde_json::from_str(&response)?;
|
||||||
|
|
||||||
|
let mut sp_member_data: HashMap<String, String> = HashMap::new();
|
||||||
|
for member in json {
|
||||||
|
sp_member_data.insert(
|
||||||
|
member["content"]["name"].to_string(),
|
||||||
|
member["id"].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(sp_member_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_member_id(m: &Member, ids: &HashMap<String, String>) -> Result<String> {
|
||||||
|
|
||||||
|
for (sp_name, sp_id) in ids {
|
||||||
|
if &m.name == sp_name || m.aliases.iter().any(|e| sp_name.contains(e)) {
|
||||||
|
return Ok(sp_id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(eyre!(format!("SP ID not found for {}", &m.name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fronters(key: &str, sys: &System) -> Result<Vec<Member>> {
|
||||||
|
let url = format!("{}/fronters", SP_URL);
|
||||||
|
|
||||||
|
let result = http_get(url,key)?;
|
||||||
|
let json: Vec<Value> = serde_json::from_str(&result)?;
|
||||||
|
|
||||||
|
let mut members = Vec::new();
|
||||||
|
for data in json {
|
||||||
|
let sp_id = &data["content"]["member"].to_string();
|
||||||
|
for member in &sys.members {
|
||||||
|
if &member.sp_id == sp_id {
|
||||||
|
members.push(member.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_front_id(key: &str, to_id: &Member) -> Result<String> {
|
||||||
|
let url = format!("{}/fronters", SP_URL);
|
||||||
|
|
||||||
|
let result = http_get(url,key)?;
|
||||||
|
let datas: Vec<Value> = serde_json::from_str(&result)?;
|
||||||
|
|
||||||
|
for data in datas {
|
||||||
|
let sp_f_id = &data["id"].to_string();
|
||||||
|
let sp_id = &data["content"]["member"].to_string();
|
||||||
|
if sp_id.to_string() == to_id.sp_id {
|
||||||
|
return Ok(sp_f_id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(eyre!("SP ID not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fronters(key: &str, to_front: &Vec<Member>, fronters: &Fronters) -> Result<()> {
|
||||||
|
if to_front == &fronters.sp {
|
||||||
|
println!("Members already fonting");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
for fronting_member in &fronters.sp {
|
||||||
|
if !to_front.contains(&fronting_member) {
|
||||||
|
let f_id = get_front_id(&key, &fronting_member)?;
|
||||||
|
|
||||||
|
let url = format!("{}/frontHistory/{}", SP_URL, f_id);
|
||||||
|
|
||||||
|
#[derive (Serialize)]
|
||||||
|
#[allow (non_snake_case)]
|
||||||
|
struct SpRem {
|
||||||
|
live: bool,
|
||||||
|
endTime: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
let end_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)?.as_secs();
|
||||||
|
let rem = SpRem {
|
||||||
|
live: false,
|
||||||
|
endTime: end_time * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = serde_json::to_string(&rem)?;
|
||||||
|
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let rb = client
|
||||||
|
.patch(url)
|
||||||
|
.body(body)
|
||||||
|
.header("content-type", "application/json; charset=utf-8")
|
||||||
|
.header(USER_AGENT, "Pluralsync")
|
||||||
|
.header(AUTHORIZATION, key);
|
||||||
|
|
||||||
|
match http_request(rb) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("{}", e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tf_member in to_front {
|
||||||
|
if !fronters.sp.contains(&tf_member) {
|
||||||
|
let url = format!("{}/frontHistory", SP_URL);
|
||||||
|
|
||||||
|
#[derive (Serialize)]
|
||||||
|
#[allow (non_snake_case)]
|
||||||
|
struct SpAdd {
|
||||||
|
member: String,
|
||||||
|
custom: bool,
|
||||||
|
live: bool,
|
||||||
|
startTime: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)?.as_secs();
|
||||||
|
let rem = SpAdd {
|
||||||
|
member: String::from(&tf_member.sp_id),
|
||||||
|
custom: false,
|
||||||
|
live: true,
|
||||||
|
startTime: start_time * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = serde_json::to_string(&rem)?;
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let rb = client
|
||||||
|
.post(url)
|
||||||
|
.body(body)
|
||||||
|
.header("content-type", "application/json; charset=utf-8")
|
||||||
|
.header(USER_AGENT, "Pluralsync")
|
||||||
|
.header(AUTHORIZATION, key);
|
||||||
|
|
||||||
|
match http_request(rb) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("{}", e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod request {
|
||||||
|
|
||||||
|
use reqwest::RequestBuilder;
|
||||||
|
use reqwest::header::{USER_AGENT, AUTHORIZATION};
|
||||||
|
#[cfg(feature = "fedi")]
|
||||||
|
use reqwest::multipart::{Part, Form};
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn http_get(url: String, key: &str) -> Result<String> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let rb = client
|
||||||
|
.get(url)
|
||||||
|
.header(USER_AGENT, "Pluralsync")
|
||||||
|
.header(AUTHORIZATION, key);
|
||||||
|
let res = rb.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn http_request(rb: RequestBuilder) -> Result<String> {
|
||||||
|
let res = rb.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
use crate::configdata::*;
|
||||||
|
use crate::systemdata::*;
|
||||||
|
use crate::clap_ps::ForceFrom;
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
|
pub enum CurrentScreen {
|
||||||
|
Dash,
|
||||||
|
Configuration,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ConfigEdit {
|
||||||
|
PkKey,
|
||||||
|
SpKey,
|
||||||
|
AvatarEnabled,
|
||||||
|
AvatarFolder,
|
||||||
|
AvatarOutput,
|
||||||
|
AvatarBlacklist,
|
||||||
|
DiscEnabled,
|
||||||
|
DiscToken,
|
||||||
|
FediEnabled,
|
||||||
|
FediInstance,
|
||||||
|
FediToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
pub input: String,
|
||||||
|
pub cfg_dir: String,
|
||||||
|
pub cfg: Option<Config>,
|
||||||
|
pub sys: Option<System>,
|
||||||
|
pub fronters: Option<Fronters>,
|
||||||
|
pub current_screen: Option<CurrentScreen>,
|
||||||
|
pub editing: Option<ConfigEdit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new(confpath: String) -> App {
|
||||||
|
App {
|
||||||
|
input: String::new(),
|
||||||
|
cfg_dir: confpath,
|
||||||
|
cfg: None,
|
||||||
|
sys: None,
|
||||||
|
fronters: None,
|
||||||
|
current_screen: None,
|
||||||
|
editing: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_system(&mut self, update: bool) -> Result<()> {
|
||||||
|
|
||||||
|
match System::get_system(&self, update) {
|
||||||
|
Ok(s) => {
|
||||||
|
self.sys = Some(s);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
self.sys = None;
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_config(&mut self) -> Result<()> {
|
||||||
|
|
||||||
|
match Config::get_config(&self.cfg_dir) {
|
||||||
|
Ok(c) => {
|
||||||
|
self.cfg = Some(c);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
self.cfg = None;
|
||||||
|
Err(e)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_fronters(&mut self, ff: ForceFrom) -> Result<()> {
|
||||||
|
|
||||||
|
self.fronters = Some(Fronters::get(self, ff)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fronters(&mut self, to_front: Vec<Member>) -> Result<()> {
|
||||||
|
|
||||||
|
self.get_fronters(ForceFrom::None)?;
|
||||||
|
|
||||||
|
self.fronters = Some(Fronters::set(self, to_front)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,10 +19,13 @@ pub struct Args {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
///Fetches the system member information from PluralKit and SimplyPlural
|
/// Fetches the system member information from PluralKit and SimplyPlural
|
||||||
Sync,
|
Sync,
|
||||||
|
/// TUI configuration tool
|
||||||
|
TUI,
|
||||||
/// Sets one or more members to the front
|
/// Sets one or more members to the front
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
|
#[non_exhaustive]
|
||||||
Set {
|
Set {
|
||||||
/// Members to set
|
/// Members to set
|
||||||
members: Vec<String>,
|
members: Vec<String>,
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use std::{
|
||||||
|
fs::create_dir,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
use crate::utils::*;
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub pk_key: String,
|
pub pk_key: String,
|
||||||
pub sp_key: String,
|
pub sp_key: String,
|
||||||
|
@ -13,7 +23,7 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
#[cfg(feature = "avatar")]
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct AvatarModule {
|
pub struct AvatarModule {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub avatar_folder: String,
|
pub avatar_folder: String,
|
||||||
|
@ -22,7 +32,7 @@ pub struct AvatarModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "discord")]
|
#[cfg(feature = "discord")]
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct DiscModule {
|
pub struct DiscModule {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
|
@ -31,9 +41,29 @@ pub struct DiscModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fedi")]
|
#[cfg(feature = "fedi")]
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct FediModule {
|
pub struct FediModule {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub instance: String,
|
pub instance: String,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn get_config(cfg_dir: &str) -> Result<Config> {
|
||||||
|
|
||||||
|
let cfg_file_path = format!("{}/config.json", cfg_dir);
|
||||||
|
|
||||||
|
if !Path::new(&cfg_dir).exists() {
|
||||||
|
let _ = create_dir(cfg_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if Path::new(&cfg_file_path).exists() {
|
||||||
|
let json_config: Value = load_json(&cfg_file_path)?;
|
||||||
|
let conf: Config = serde_json::from_value(json_config)?;
|
||||||
|
Ok(conf)
|
||||||
|
} else {
|
||||||
|
// TODO Trigger file creation screen if there is no config file.
|
||||||
|
Err(eyre!("Config file not found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
596
src/main.rs
596
src/main.rs
|
@ -1,187 +1,85 @@
|
||||||
|
mod app;
|
||||||
|
mod configdata;
|
||||||
|
mod systemdata;
|
||||||
|
mod api;
|
||||||
|
mod utils;
|
||||||
|
mod clap_ps;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
#[cfg(feature = "avatar")]
|
#[cfg(feature = "avatar")]
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
use std::fs::create_dir;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "avatar")]
|
#[cfg(feature = "avatar")]
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use reqwest::RequestBuilder;
|
|
||||||
use reqwest::header::{USER_AGENT, AUTHORIZATION};
|
|
||||||
#[cfg(feature = "fedi")]
|
|
||||||
use reqwest::multipart::{Part, Form};
|
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use dirs;
|
use dirs;
|
||||||
|
|
||||||
mod configdata;
|
use app::*;
|
||||||
|
|
||||||
use configdata::*;
|
use configdata::*;
|
||||||
|
|
||||||
mod systemdata;
|
|
||||||
use systemdata::*;
|
use systemdata::*;
|
||||||
|
|
||||||
mod clap_ps;
|
|
||||||
use clap_ps::*;
|
use clap_ps::*;
|
||||||
use clap::Parser;
|
use clap::{builder::TypedValueParser, Parser};
|
||||||
|
|
||||||
const PK_URL: &str = "https://api.pluralkit.me/v2";
|
use color_eyre::eyre::{eyre, Result};
|
||||||
const SP_URL: &str = "https://api.apparyllis.com/v1";
|
|
||||||
|
|
||||||
const EXAMPLE_JSON: &str = r#"{
|
fn main() -> Result<()> {
|
||||||
"pk_key": "// Pluralkit token",
|
|
||||||
"sp_key": "// Simplplural token",
|
|
||||||
"avatar_module": {
|
|
||||||
"enabled": false,
|
|
||||||
"avatar_folder": "// Folder to grab profile pictures, they follow they member1member2...memberX.png format",
|
|
||||||
"avatar_output_path": "// Path for the copied selected avatar",
|
|
||||||
"blacklist": "// Array of members to not include in the avatar module"
|
|
||||||
},
|
|
||||||
"disc_module": {
|
|
||||||
"enabled": false,
|
|
||||||
"token": "// Discord user token",
|
|
||||||
"python_path": "// Path to the python executable",
|
|
||||||
"script_path": "// Path to updatediscordavatar.py"
|
|
||||||
},
|
|
||||||
"fedi_module": {
|
|
||||||
"enabled": false,
|
|
||||||
"instance" : "// Fedi instance url",
|
|
||||||
"token": "// Fedi bearer token"
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
|
|
||||||
let cli = Args::parse();
|
let cli = Args::parse();
|
||||||
|
|
||||||
let config_path = match dirs::config_dir() {
|
let config_path = match dirs::config_dir() {
|
||||||
Some(d) => format!("{}/pluralsync", d.display()),
|
Some(d) => format!("{}/pluralsync", d.display()),
|
||||||
None => {
|
None => {
|
||||||
println!("Could not fetch the config directory");
|
return Err(eyre!("Could not figure out the config directory!"));
|
||||||
return ();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res: Result<(), &str> = Ok(());
|
let mut app: App = App::new(config_path.clone());
|
||||||
|
app.load_config();
|
||||||
|
app.load_system(false);
|
||||||
|
|
||||||
match cli.cmd {
|
match cli.cmd {
|
||||||
Commands::Sync => {
|
Commands::Sync => {
|
||||||
res = sync(config_path);
|
app.load_system(true)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
// SET MEMBER
|
Commands::TUI => {
|
||||||
#[cfg(all(feature = "discord", feature = "fedi"))]
|
()
|
||||||
Commands::Set { members, discord, fedi } => {
|
},
|
||||||
res = set_member(config_path.clone(), members);
|
|
||||||
|
Commands::Set { members, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => {
|
||||||
|
let res = set_member(&app, members);
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
#[cfg(feature = "avatar")] {
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let _ = update_avatars(config_path.clone(), discord, fedi);
|
let _ = update_avatars(config_path.clone(), #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi);
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(feature = "discord", not(feature = "fedi")))]
|
|
||||||
Commands::Set { members, discord } => {
|
|
||||||
res = set_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), discord, false);
|
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), feature = "fedi"))]
|
|
||||||
Commands::Set { members, fedi} => {
|
|
||||||
res = set_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), false, fedi);
|
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), not(feature = "fedi")))]
|
|
||||||
Commands::Set { members } => {
|
|
||||||
res = set_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), false, false);
|
|
||||||
},
|
},
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// ADD MEMBER
|
Commands::Add { members, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => {
|
||||||
#[cfg(all(feature = "discord", feature = "fedi"))]
|
let res = add_member(config_path.clone(), members);
|
||||||
Commands::Add { members, discord, fedi } => {
|
|
||||||
res = add_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
#[cfg(feature = "avatar")] {
|
||||||
match res {
|
match res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let _ = update_avatars(config_path.clone(), discord, fedi);
|
let _ = update_avatars(config_path.clone(), #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi);
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(feature = "discord", not(feature = "fedi")))]
|
|
||||||
Commands::Add { members, discord } => {
|
|
||||||
res = add_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), discord, false);
|
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), feature = "fedi"))]
|
|
||||||
Commands::Add { members, fedi} => {
|
|
||||||
res = add_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), false, fedi);
|
|
||||||
},
|
|
||||||
Err(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), not(feature = "fedi")))]
|
|
||||||
Commands::Add { members } => {
|
|
||||||
res = add_member(config_path.clone(), members);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")] {
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
let _ = update_avatars(config_path.clone(), false, false);
|
|
||||||
},
|
},
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Get MEMBER
|
Commands::Get { force_from, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => {
|
||||||
#[cfg(all(feature = "discord", feature = "fedi"))]
|
|
||||||
Commands::Get { force_from, discord, fedi } => {
|
|
||||||
let ff = match force_from {
|
let ff = match force_from {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => ForceFrom::None,
|
None => ForceFrom::None,
|
||||||
|
@ -190,136 +88,40 @@ fn main() {
|
||||||
let _ = get(config_path.clone(), ff);
|
let _ = get(config_path.clone(), ff);
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
#[cfg(feature = "avatar")]
|
||||||
let _ = update_avatars(config_path.clone(), discord, fedi);
|
let _ = update_avatars(config_path.clone(), #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi);
|
||||||
},
|
|
||||||
#[cfg(all(feature = "discord", not(feature = "fedi")))]
|
|
||||||
Commands::Get { force_from, discord } => {
|
|
||||||
let ff = match force_from {
|
|
||||||
Some(x) => x,
|
|
||||||
None => ForceFrom::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = get(config_path.clone(), ff);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
|
||||||
let _ = update_avatars(config_path.clone(), discord, false);
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), feature = "fedi"))]
|
|
||||||
Commands::Get { force_from, fedi} => {
|
|
||||||
let ff = match force_from {
|
|
||||||
Some(x) => x,
|
|
||||||
None => ForceFrom::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = get(config_path.clone(), ff);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
|
||||||
let _ = update_avatars(config_path.clone(), false, fedi);
|
|
||||||
},
|
|
||||||
#[cfg(all(not(feature = "discord"), not(feature = "fedi")))]
|
|
||||||
Commands::Get { force_from } => {
|
|
||||||
let ff = match force_from {
|
|
||||||
Some(x) => x,
|
|
||||||
None => ForceFrom::None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = get(config_path.clone(), ff);
|
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
|
||||||
let _ = update_avatars(config_path.clone(), false, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Commands::Members => {
|
Commands::Members => {
|
||||||
res = memberlist(config_path);
|
let _res = memberlist(config_path);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => println!("{}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sync(config_path: String) -> Result<(), &'static str> {
|
|
||||||
// Get config
|
|
||||||
let config: Config = match get_config(&config_path) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => return Err(e)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get Pluralkit system id
|
|
||||||
let pk_sys = pk_get_system(&config.pk_key);
|
|
||||||
let pk_sysid = pk_sys["id"].as_str().unwrap();
|
|
||||||
|
|
||||||
// Get Simplyplural user id
|
|
||||||
let sp_user_id = sp_get_userid(&config.sp_key);
|
|
||||||
|
|
||||||
// Get Simplyplural member ids
|
|
||||||
let sp_member_ids = sp_get_memberids(&config.sp_key, &sp_user_id);
|
|
||||||
|
|
||||||
// get members
|
|
||||||
let pk_members = pk_get_members(&config.pk_key, pk_sysid);
|
|
||||||
let mut members: Vec<Member> = Vec::new();
|
|
||||||
for member in pk_members {
|
|
||||||
let mut m = Member {
|
|
||||||
pk_id: member["id"].as_str().unwrap().to_string(),
|
|
||||||
sp_id: String::new(),
|
|
||||||
name: member["name"].as_str().unwrap().to_string(),
|
|
||||||
alias: String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
if member["display_name"].as_str() != None {
|
|
||||||
m.alias = member["display_name"].as_str().unwrap().to_string();
|
|
||||||
} else {
|
|
||||||
m.alias = String::from(&m.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
m.sp_id = get_sp_id(&m, &sp_member_ids);
|
|
||||||
|
|
||||||
members.push(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sys = System {
|
|
||||||
pk_userid: pk_sysid.to_string(),
|
|
||||||
sp_userid: sp_user_id,
|
|
||||||
members: members.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let json = serde_json::to_string(&sys);
|
|
||||||
let _ = fs::write(format!("{}/system.json", config_path), &json.unwrap());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_member(config_path: String, tf_members: Vec<String>) -> Result<(), &'static str> {
|
fn set_member(app: &App, tf_members: Vec<String>) -> Result<()> {
|
||||||
let config: Config = match get_config(&config_path) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => return Err(e)
|
|
||||||
};
|
|
||||||
|
|
||||||
let system: System = get_system(&config_path);
|
let system = &app.sys.ok_or(eyre!("No system data found"))?;
|
||||||
|
|
||||||
let mut to_front: Vec<Member> = Vec::new();
|
let mut to_front: Vec<Member> = Vec::new();
|
||||||
for member in &tf_members {
|
for member in &tf_members {
|
||||||
for mem in &system.members {
|
for mem in &system.members {
|
||||||
if mem.name.to_lowercase() == member.to_lowercase() || mem.alias.to_lowercase() == member.to_lowercase() {
|
let lowcase_aliases: Vec<String> = mem.aliases.iter().map(|x| x.to_lowercase()).collect();
|
||||||
println!("Member {member} found");
|
if mem.name.to_lowercase() == member.to_lowercase() || lowcase_aliases.iter().any(|e| member.contains(e)) {
|
||||||
|
println!("Member {member} found.");
|
||||||
to_front.push(mem.clone());
|
to_front.push(mem.clone());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if to_front.len() == tf_members.len() {
|
|
||||||
let fronters = get_fronters(&config.pk_key, &config.sp_key, &system, ForceFrom::None);
|
|
||||||
|
|
||||||
pk_set_fronters(&config.pk_key, &system, &to_front, &fronters);
|
if to_front.len() == tf_members.len() {
|
||||||
sp_set_fronters(&config.sp_key, &to_front, &fronters);
|
app.set_fronters(to_front)?;
|
||||||
} else {
|
} BOOKMARK else {
|
||||||
println!("One or more members were not found. Known members:\n--------------------------");
|
println!("One or more members were not found. Known members:\n--------------------------");
|
||||||
let _ = memberlist(config_path);
|
let _ = memberlist(app);
|
||||||
println!("--------------------------\nIf a member is missing from the system try running \"pluralsync sync\" to refresh the local database");
|
println!("--------------------------\nIf a member is missing from the system try running \"pluralsync sync\" to refresh the local database");
|
||||||
return Err("Missing member");
|
return Err(eyre!("Missing member"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "jlog")] {
|
#[cfg(feature = "jlog")] {
|
||||||
|
@ -328,8 +130,10 @@ fn set_member(config_path: String, tf_members: Vec<String>) -> Result<(), &'stat
|
||||||
names.push(String::from(&m.name));
|
names.push(String::from(&m.name));
|
||||||
}
|
}
|
||||||
let log_fronters = names.join(" || ");
|
let log_fronters = names.join(" || ");
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
std::process::Command::new("jlog").args(["info", format!("Switch registered: {}", log_fronters)]).output().expect("Logging error");
|
std::process::Command::new("jlog").args(["info", format!("Switch registered: {}", log_fronters)]).output().expect("Logging error");
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
std::process::Command::new("jlog").arg("info").arg(format!("Switch registered: {}", log_fronters)).output().expect("Logging error");
|
std::process::Command::new("jlog").arg("info").arg(format!("Switch registered: {}", log_fronters)).output().expect("Logging error");
|
||||||
}
|
}
|
||||||
|
@ -425,7 +229,7 @@ fn get(config_path: String, ff: ForceFrom) -> Result<Vec<String>, &'static str>
|
||||||
|
|
||||||
#[cfg(feature = "avatar")]
|
#[cfg(feature = "avatar")]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn update_avatars(config_path: String, discord: bool, fedi: bool) -> Result<(), &'static str>{
|
fn update_avatars(config_path: String, #[cfg(feature = "discord")] discord: bool, #[cfg(feature = "fedi")] fedi: bool) -> Result<(), &'static str>{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut config: Config = match get_config(&config_path) {
|
let mut config: Config = match get_config(&config_path) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
|
@ -523,317 +327,3 @@ fn fedi_module(config: &Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pk_get_system(key: &str) -> Value {
|
|
||||||
let url = format!("{}/systems/@me", PK_URL);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
return serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pk_get_members(key: &str, sysid: &str) -> Vec<Value> {
|
|
||||||
let url = format!("{}/systems/{}/members", PK_URL, sysid);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
|
|
||||||
return datas;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pk_get_fronters(key: &str, sys: &System) -> Vec<Member> {
|
|
||||||
let url = format!("{}/systems/{}/fronters", PK_URL, sys.pk_userid);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let data: Value = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
let memberdata = &data["members"].as_array();
|
|
||||||
|
|
||||||
let mut members: Vec<Member> = Vec::new();
|
|
||||||
for member in memberdata {
|
|
||||||
for m in member.into_iter() {
|
|
||||||
for dbmem in &sys.members {
|
|
||||||
if m["name"].as_str().unwrap() == dbmem.name {
|
|
||||||
members.push(dbmem.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pk_set_fronters(key: &str, sys: &System, to_front: &Vec<Member>, fronters: &Fronters) {
|
|
||||||
let url = format!("{}/systems/{}/switches", PK_URL, sys.pk_userid);
|
|
||||||
|
|
||||||
if to_front != &fronters.pk {
|
|
||||||
let mut frontcodes = Vec::new();
|
|
||||||
for tf in to_front {
|
|
||||||
frontcodes.push(String::from(&tf.pk_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut body: HashMap<&str, Vec<String>> = HashMap::new();
|
|
||||||
body.insert("members", frontcodes);
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let rb = client
|
|
||||||
.post(url)
|
|
||||||
.json(&body)
|
|
||||||
.header("content-type", "application/json; charset=utf-8")
|
|
||||||
.header(USER_AGENT, "Pluralsync")
|
|
||||||
.header(AUTHORIZATION, key);
|
|
||||||
|
|
||||||
match http_request(rb) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => println!("{}", e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
println!("Members already fonting");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sp_get_userid(key: &str) -> String {
|
|
||||||
let url = format!("{}/me", SP_URL);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let json_res : Value = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
return json_res["id"].as_str().unwrap().to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sp_get_memberids(key: &str, system_id: &str) -> HashMap<String, String> {
|
|
||||||
let url = format!("{}/members/{}", SP_URL, system_id);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
|
|
||||||
let mut sp_memberdata: HashMap<String, String> = HashMap::new();
|
|
||||||
for data in datas {
|
|
||||||
sp_memberdata.insert(String::from(data["content"]["name"].as_str().unwrap()), String::from(data["id"].as_str().unwrap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sp_memberdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sp_id(mem: &Member, ids: &HashMap<String, String>) -> String {
|
|
||||||
|
|
||||||
let mut member_id = String::new();
|
|
||||||
|
|
||||||
for (mn, mid) in ids {
|
|
||||||
if &mem.name == mn || &mem.alias == mn {
|
|
||||||
member_id = String::from(mid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return member_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sp_get_fronters(key: &str, sys: &System) -> Vec<Member> {
|
|
||||||
let url = format!("{}/fronters", SP_URL);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
|
|
||||||
let mut members = Vec::new();
|
|
||||||
for data in datas {
|
|
||||||
let sp_id = &data["content"]["member"].as_str().unwrap();
|
|
||||||
for member in &sys.members {
|
|
||||||
if &member.sp_id == sp_id {
|
|
||||||
members.push(member.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sp_get_frontids(key: &str, to_id: &Member) -> String {
|
|
||||||
let url = format!("{}/fronters", SP_URL);
|
|
||||||
|
|
||||||
let res = http_get(url,key);
|
|
||||||
let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap();
|
|
||||||
|
|
||||||
for data in datas {
|
|
||||||
let sp_f_id = &data["id"].as_str().unwrap();
|
|
||||||
let sp_id = &data["content"]["member"].as_str().unwrap();
|
|
||||||
if sp_id.to_string() == to_id.sp_id {
|
|
||||||
return sp_f_id.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return String::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sp_set_fronters(key: &str, to_front: &Vec<Member>, fronters: &Fronters) {
|
|
||||||
if to_front == &fronters.sp {
|
|
||||||
println!("Members already fonting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for fronting_member in &fronters.sp {
|
|
||||||
if !to_front.contains(&fronting_member) {
|
|
||||||
let f_id = sp_get_frontids(&key, &fronting_member);
|
|
||||||
|
|
||||||
let url = format!("{}/frontHistory/{}", SP_URL, f_id);
|
|
||||||
|
|
||||||
#[derive (Serialize)]
|
|
||||||
#[allow (non_snake_case)]
|
|
||||||
struct SpRem {
|
|
||||||
live: bool,
|
|
||||||
endTime: u64
|
|
||||||
}
|
|
||||||
|
|
||||||
let end_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).expect("wa").as_secs();
|
|
||||||
let rem = SpRem {
|
|
||||||
live: false,
|
|
||||||
endTime: end_time * 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
let body = serde_json::to_string(&rem).expect("Error");
|
|
||||||
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let rb = client
|
|
||||||
.patch(url)
|
|
||||||
.body(body)
|
|
||||||
.header("content-type", "application/json; charset=utf-8")
|
|
||||||
.header(USER_AGENT, "Pluralsync")
|
|
||||||
.header(AUTHORIZATION, key);
|
|
||||||
|
|
||||||
match http_request(rb) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => println!("{}", e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for tf_member in to_front {
|
|
||||||
if !fronters.sp.contains(&tf_member) {
|
|
||||||
let url = format!("{}/frontHistory", SP_URL);
|
|
||||||
|
|
||||||
#[derive (Serialize)]
|
|
||||||
#[allow (non_snake_case)]
|
|
||||||
struct SpAdd {
|
|
||||||
member: String,
|
|
||||||
custom: bool,
|
|
||||||
live: bool,
|
|
||||||
startTime: u64
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).expect("wa").as_secs();
|
|
||||||
let rem = SpAdd {
|
|
||||||
member: String::from(&tf_member.sp_id),
|
|
||||||
custom: false,
|
|
||||||
live: true,
|
|
||||||
startTime: start_time * 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
let body = serde_json::to_string(&rem).expect("Error");
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let rb = client
|
|
||||||
.post(url)
|
|
||||||
.body(body)
|
|
||||||
.header("content-type", "application/json; charset=utf-8")
|
|
||||||
.header(USER_AGENT, "Pluralsync")
|
|
||||||
.header(AUTHORIZATION, key);
|
|
||||||
|
|
||||||
match http_request(rb) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => println!("{}", e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_json(path: String) -> Value {
|
|
||||||
if Path::new(&path).exists() {
|
|
||||||
let config_data = fs::read_to_string(&path).expect("File not found");
|
|
||||||
return serde_json::from_str(&config_data).unwrap();
|
|
||||||
} else {
|
|
||||||
println!("Config file in {path} not found");
|
|
||||||
return Value::Null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_config(config_path: &str) -> Result<Config, &'static str> {
|
|
||||||
let path = format!("{}/config.json", config_path);
|
|
||||||
if Path::new(config_path).exists() {
|
|
||||||
|
|
||||||
let result = load_json(String::from(&path));
|
|
||||||
|
|
||||||
if result == Value::Null {
|
|
||||||
let _ = fs::write(path, EXAMPLE_JSON);
|
|
||||||
return Err("Config file missing, creating template in {path}");
|
|
||||||
} else {
|
|
||||||
let config: Config = serde_json::from_value(result).expect("Error unwrapping");
|
|
||||||
return Ok(config);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let _ = create_dir(config_path);
|
|
||||||
let _ = fs::write(path, EXAMPLE_JSON);
|
|
||||||
return Err("Directory {config_path} does not exist. Creating with template config");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_system(config_path: &str) -> System {
|
|
||||||
let path = format!("{}/system.json", config_path);
|
|
||||||
|
|
||||||
let mut result = load_json(String::from(&path));
|
|
||||||
|
|
||||||
if result == Value::Null {
|
|
||||||
println!("Syncing system config");
|
|
||||||
let _ = sync(String::from(config_path));
|
|
||||||
result = load_json(String::from(&path));
|
|
||||||
}
|
|
||||||
|
|
||||||
let vec = serde_json::to_vec(&result).unwrap();
|
|
||||||
let sys = serde_json::from_slice::<System>(&vec).unwrap();
|
|
||||||
|
|
||||||
return sys;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_fronters(pk_key: &str, sp_key: &str, sys: &System, ff: ForceFrom) -> Fronters {
|
|
||||||
let mut fronters = Fronters::new(
|
|
||||||
pk_get_fronters(pk_key, sys),
|
|
||||||
sp_get_fronters(sp_key, sys)
|
|
||||||
);
|
|
||||||
|
|
||||||
if fronters.pk != fronters.sp {
|
|
||||||
match ff {
|
|
||||||
ForceFrom::PK => {
|
|
||||||
fronters.sp = fronters.pk.clone()
|
|
||||||
}
|
|
||||||
ForceFrom::SP => {
|
|
||||||
fronters.pk = fronters.sp.clone()
|
|
||||||
}
|
|
||||||
ForceFrom::None => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fronters;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn http_get(url: String, key: &str) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let rb = client
|
|
||||||
.get(url)
|
|
||||||
.header(USER_AGENT, "Pluralsync")
|
|
||||||
.header(AUTHORIZATION, key);
|
|
||||||
let res = rb.send()
|
|
||||||
.await?
|
|
||||||
.text()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn http_request(rb: RequestBuilder) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let res = rb.send()
|
|
||||||
.await?
|
|
||||||
.text()
|
|
||||||
.await?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
|
||||||
|
use crate::api::{pk, sp};
|
||||||
|
use crate::utils::load_json;
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::clap_ps::ForceFrom;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct System {
|
pub struct System {
|
||||||
|
@ -12,7 +23,77 @@ pub struct Member {
|
||||||
pub pk_id: String,
|
pub pk_id: String,
|
||||||
pub sp_id: String,
|
pub sp_id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub alias: String
|
pub aliases: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn get_system(app: &App, update: bool) -> Result<System>{
|
||||||
|
|
||||||
|
let sys_file_path = format!("{}/system.json", app.cfg_dir);
|
||||||
|
|
||||||
|
if Path::new(&sys_file_path).exists() && !update {
|
||||||
|
let json_system: Value = load_json(&sys_file_path)?;
|
||||||
|
let sys: System = serde_json::from_value(json_system)?;
|
||||||
|
Ok(sys)
|
||||||
|
} else {
|
||||||
|
match Self::update_system(app) {
|
||||||
|
Ok(s) => {
|
||||||
|
Ok(s)
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_system(app: &App) -> Result<System> {
|
||||||
|
|
||||||
|
if let Some(conf) = &app.cfg {
|
||||||
|
|
||||||
|
// PluralKit
|
||||||
|
let pk_sys = pk::get_system(&conf.pk_key)?;
|
||||||
|
let pk_id = pk_sys["id"].to_string();
|
||||||
|
let pk_members = pk::get_members(&conf.pk_key, &pk_id)?;
|
||||||
|
|
||||||
|
// Simplyplural
|
||||||
|
let sp_user_id = sp::get_user_id(&conf.sp_key)?;
|
||||||
|
let sp_member_ids = sp::get_member_ids(&conf.sp_key, &sp_user_id)?;
|
||||||
|
|
||||||
|
// Get members
|
||||||
|
let mut members: Vec<Member> = Vec::new();
|
||||||
|
for pk_mem in pk_members {
|
||||||
|
let mut m = Member {
|
||||||
|
pk_id: pk_mem["id"].to_string(),
|
||||||
|
sp_id: String::new(),
|
||||||
|
name: pk_mem["name"].to_string(),
|
||||||
|
aliases: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if pk_mem["display_name"].as_str() != None {
|
||||||
|
m.aliases.push(pk_mem["display_name"].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
m.sp_id = sp::get_member_id(&m, &sp_member_ids)?;
|
||||||
|
|
||||||
|
members.push(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create system
|
||||||
|
let sys = System {
|
||||||
|
pk_userid: pk_id.to_string(),
|
||||||
|
sp_userid: sp_user_id,
|
||||||
|
members: members.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&sys)?;
|
||||||
|
fs::write(format!("{}/system.json", &app.cfg_dir), &json)?;
|
||||||
|
|
||||||
|
return Ok(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(eyre!("No configuration loaded!"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -25,4 +106,44 @@ impl Fronters {
|
||||||
pub fn new(pk: Vec<Member>, sp: Vec<Member>) -> Self {
|
pub fn new(pk: Vec<Member>, sp: Vec<Member>) -> Self {
|
||||||
Self {sp, pk}
|
Self {sp, pk}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get(app: &App, ff: ForceFrom) -> Result<Self> {
|
||||||
|
if let Some(cfg) = &app.cfg {
|
||||||
|
if let Some(sys) = &app.sys {
|
||||||
|
let mut fronters = Fronters::new(
|
||||||
|
pk::get_fronters(&cfg.pk_key, &sys)?,
|
||||||
|
sp::get_fronters(&cfg.sp_key, &sys)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
if fronters.pk != fronters.sp {
|
||||||
|
match ff {
|
||||||
|
ForceFrom::SP => {
|
||||||
|
fronters.pk = fronters.sp.clone();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
fronters.sp = fronters.pk.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(fronters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(eyre!("System or configuration missing"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(app: &App, to_front: Vec<Member>) -> Result<Fronters> {
|
||||||
|
|
||||||
|
if let Some(cfg) = &app.cfg {
|
||||||
|
if let Some(sys) = &app.sys {
|
||||||
|
if let Some(fronters) = &app.fronters {
|
||||||
|
pk::set_fronters(&cfg.pk_key, &sys, &to_front, &fronters)?;
|
||||||
|
sp::set_fronters(&cfg.sp_key, &to_front, &fronters)?;
|
||||||
|
|
||||||
|
return Ok(Fronters::new(to_front.clone(), to_front.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(eyre!("Missing app information"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use std::fs;
|
||||||
|
use serde_json::Value;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
|
pub fn load_json(file_path: &str) -> Result<Value> {
|
||||||
|
|
||||||
|
let file_data = fs::read_to_string(&file_path)?;
|
||||||
|
let json_value: Value = serde_json::from_str(&file_data)?;
|
||||||
|
|
||||||
|
Ok(json_value)
|
||||||
|
}
|
Loading…
Reference in New Issue