Added the full Radarr CLI so users can programmatically access all the same management features as in the TUI
This commit is contained in:
Generated
+236
-3
@@ -76,9 +76,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.7"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
@@ -130,6 +130,33 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert_cmd"
|
||||||
|
version = "2.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"bstr",
|
||||||
|
"doc-comment",
|
||||||
|
"libc",
|
||||||
|
"predicates",
|
||||||
|
"predicates-core",
|
||||||
|
"predicates-tree",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -162,6 +189,9 @@ name = "bimap"
|
|||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
|
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -175,6 +205,17 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
@@ -214,6 +255,12 @@ 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 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.38"
|
version = "0.4.38"
|
||||||
@@ -229,6 +276,55 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_complete"
|
||||||
|
version = "4.5.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -328,6 +424,16 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
@@ -360,6 +466,12 @@ version = "0.1.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "difflib"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
@@ -381,6 +493,18 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "doc-comment"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@@ -448,6 +572,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fragile"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
@@ -887,19 +1017,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.0.35"
|
version = "0.0.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"assert_cmd",
|
||||||
|
"async-trait",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bimap",
|
"bimap",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"clap",
|
||||||
|
"clap_complete",
|
||||||
|
"colored",
|
||||||
"confy",
|
"confy",
|
||||||
"crossterm 0.27.0",
|
"crossterm 0.27.0",
|
||||||
|
"ctrlc",
|
||||||
"derivative",
|
"derivative",
|
||||||
"human-panic",
|
"human-panic",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
|
"mockall",
|
||||||
"mockito",
|
"mockito",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
@@ -962,6 +1100,32 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mockall"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"downcast",
|
||||||
|
"fragile",
|
||||||
|
"mockall_derive",
|
||||||
|
"predicates",
|
||||||
|
"predicates-tree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mockall_derive"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mockito"
|
name = "mockito"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -998,6 +1162,18 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1172,6 +1348,33 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"difflib",
|
||||||
|
"predicates-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-core"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "predicates-tree"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
|
||||||
|
dependencies = [
|
||||||
|
"predicates-core",
|
||||||
|
"termtree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty_assertions"
|
name = "pretty_assertions"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -1612,6 +1815,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.26.3"
|
version = "0.26.3"
|
||||||
@@ -1695,6 +1904,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termtree"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.63"
|
||||||
@@ -1989,6 +2204,15 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2129,6 +2353,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
|
|||||||
+16
-6
@@ -1,22 +1,24 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.0.35"
|
version = "0.0.36"
|
||||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
description = "A TUI to manage your Servarrs"
|
description = "A TUI and CLI to manage your Servarrs"
|
||||||
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"]
|
keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"]
|
||||||
documentation = "https://github.com/Dark-Alex-17/managarr"
|
documentation = "https://github.com/Dark-Alex-17/managarr"
|
||||||
repository = "https://github.com/Dark-Alex-17/managarr"
|
repository = "https://github.com/Dark-Alex-17/managarr"
|
||||||
homepage = "https://github.com/Dark-Alex-17/managarr"
|
homepage = "https://github.com/Dark-Alex-17/managarr"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.76.0"
|
rust-version = "1.82.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.68"
|
anyhow = "1.0.68"
|
||||||
backtrace = "0.3.67"
|
backtrace = "0.3.67"
|
||||||
bimap = "0.6.3"
|
bimap = { version = "0.6.3", features = ["serde"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
confy = { version = "0.6.0", default-features = false, features = ["yaml_conf"] }
|
confy = { version = "0.6.0", default-features = false, features = [
|
||||||
|
"yaml_conf",
|
||||||
|
] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
human-panic = "1.1.3"
|
human-panic = "1.1.3"
|
||||||
@@ -28,14 +30,22 @@ reqwest = { version = "0.11.14", features = ["json"] }
|
|||||||
serde_yaml = "0.9.16"
|
serde_yaml = "0.9.16"
|
||||||
serde_json = "1.0.91"
|
serde_json = "1.0.91"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
strum = {version = "0.26.1", features = ["derive"] }
|
strum = { version = "0.26.1", features = ["derive"] }
|
||||||
strum_macros = "0.26.1"
|
strum_macros = "0.26.1"
|
||||||
tokio = { version = "1.36.0", features = ["full"] }
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
tokio-util = "0.7.8"
|
tokio-util = "0.7.8"
|
||||||
ratatui = { version = "0.28.0", features = ["all-widgets"] }
|
ratatui = { version = "0.28.0", features = ["all-widgets"] }
|
||||||
urlencoding = "2.1.2"
|
urlencoding = "2.1.2"
|
||||||
|
clap = { version = "4.5.20", features = ["derive", "cargo"] }
|
||||||
|
clap_complete = "4.5.33"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
ctrlc = "3.4.5"
|
||||||
|
colored = "2.1.0"
|
||||||
|
async-trait = "0.1.83"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0.16"
|
||||||
|
mockall = "0.13.0"
|
||||||
mockito = "1.0.0"
|
mockito = "1.0.0"
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
||||||
rstest = "0.18.2"
|
rstest = "0.18.2"
|
||||||
|
|||||||
@@ -87,6 +87,70 @@ curl https://raw.githubusercontent.com/Dark-Alex-17/managarr-demo/main/managarr-
|
|||||||
|
|
||||||
- [ ] Support for Tautulli
|
- [ ] Support for Tautulli
|
||||||
|
|
||||||
|
### The Managarr CLI
|
||||||
|
Managarr can be used in one of two ways: As a TUI, or as a CLI for managing your Servarrs.
|
||||||
|
|
||||||
|
All management features available in the TUI are also available in the CLI.
|
||||||
|
|
||||||
|
The CLI can be helpful for automating tasks or for use in scripts. For example, you can use the CLI to trigger a search for a movie, or to add a movie to your library.
|
||||||
|
|
||||||
|
To see all available commands, simply run `managarr --help`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ managarr --help
|
||||||
|
managarr 0.0.36
|
||||||
|
Alex Clarke <alex.j.tusa@gmail.com>
|
||||||
|
|
||||||
|
A TUI and CLI to manage your Servarrs
|
||||||
|
|
||||||
|
Usage: managarr [COMMAND]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
radarr Commands for manging your Radarr instance
|
||||||
|
completions Generate shell completions for the Managarr CLI
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
```
|
||||||
|
|
||||||
|
All subcommands also have detailed help menus to show you how to use them. For example, to see all available commands for Radarr, you would run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ managarr radarr --help
|
||||||
|
Commands for manging your Radarr instance
|
||||||
|
|
||||||
|
Usage: managarr radarr <COMMAND>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
add Commands to add or create new resources within your Radarr instance
|
||||||
|
delete Commands to delete resources from your Radarr instance
|
||||||
|
edit Commands to edit resources in your Radarr instance
|
||||||
|
get Commands to fetch details of the resources in your Radarr instance
|
||||||
|
list Commands to list attributes from your Radarr instance
|
||||||
|
refresh Commands to refresh the data in your Radarr instance
|
||||||
|
clear-blocklist Clear the blocklist
|
||||||
|
download-release Manually download the given release for the specified movie ID
|
||||||
|
manual-search Trigger a manual search of releases for the movie with the given ID
|
||||||
|
search-new-movie Search for a new film to add to Radarr
|
||||||
|
start-task Start the specified Radarr task
|
||||||
|
test-indexer Test the indexer with the given ID. Note that a successful test returns an empty JSON body; i.e. '{}'
|
||||||
|
test-all-indexers Test all indexers
|
||||||
|
trigger-automatic-search Trigger an automatic search for the movie with the specified ID
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage Tip:** The CLI is even more powerful and useful when used in conjunction with the `jq` CLI tool. This allows you to parse the JSON response from the Managarr CLI and use it in your scripts; For example, to extract the `movieId` of the movie "Ad Astra", you would run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ managarr radarr list movies | jq '.[] | select(.title == "Ad Astra") | .id'
|
||||||
|
277
|
||||||
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|||||||
+12
-10
@@ -49,14 +49,14 @@ impl<'a> App<'a> {
|
|||||||
.dispatch_network_event(RadarrEvent::GetIndexers.into())
|
.dispatch_network_event(RadarrEvent::GetIndexers.into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetIndexerSettings.into())
|
.dispatch_network_event(RadarrEvent::GetAllIndexerSettings.into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::TestIndexer => {
|
ActiveRadarrBlock::TestIndexer => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::TestIndexer.into())
|
.dispatch_network_event(RadarrEvent::TestIndexer(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::TestAllIndexers => {
|
ActiveRadarrBlock::TestAllIndexers => {
|
||||||
@@ -72,7 +72,7 @@ impl<'a> App<'a> {
|
|||||||
.dispatch_network_event(RadarrEvent::GetQueuedEvents.into())
|
.dispatch_network_event(RadarrEvent::GetQueuedEvents.into())
|
||||||
.await;
|
.await;
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetLogs.into())
|
.dispatch_network_event(RadarrEvent::GetLogs(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::SystemUpdates => {
|
ActiveRadarrBlock::SystemUpdates => {
|
||||||
@@ -82,17 +82,17 @@ impl<'a> App<'a> {
|
|||||||
}
|
}
|
||||||
ActiveRadarrBlock::AddMovieSearchResults => {
|
ActiveRadarrBlock::AddMovieSearchResults => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::SearchNewMovie.into())
|
.dispatch_network_event(RadarrEvent::SearchNewMovie(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
|
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetMovieDetails.into())
|
.dispatch_network_event(RadarrEvent::GetMovieDetails(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::MovieHistory => {
|
ActiveRadarrBlock::MovieHistory => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetMovieHistory.into())
|
.dispatch_network_event(RadarrEvent::GetMovieHistory(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
|
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
|
||||||
@@ -102,7 +102,7 @@ impl<'a> App<'a> {
|
|||||||
|| movie_details_modal.movie_crew.items.is_empty() =>
|
|| movie_details_modal.movie_crew.items.is_empty() =>
|
||||||
{
|
{
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetMovieCredits.into())
|
.dispatch_network_event(RadarrEvent::GetMovieCredits(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -111,7 +111,7 @@ impl<'a> App<'a> {
|
|||||||
ActiveRadarrBlock::ManualSearch => match self.data.radarr_data.movie_details_modal.as_ref() {
|
ActiveRadarrBlock::ManualSearch => match self.data.radarr_data.movie_details_modal.as_ref() {
|
||||||
Some(movie_details_modal) if movie_details_modal.movie_releases.items.is_empty() => {
|
Some(movie_details_modal) if movie_details_modal.movie_releases.items.is_empty() => {
|
||||||
self
|
self
|
||||||
.dispatch_network_event(RadarrEvent::GetReleases.into())
|
.dispatch_network_event(RadarrEvent::GetReleases(None).into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -127,7 +127,9 @@ impl<'a> App<'a> {
|
|||||||
if self.data.radarr_data.prompt_confirm {
|
if self.data.radarr_data.prompt_confirm {
|
||||||
self.data.radarr_data.prompt_confirm = false;
|
self.data.radarr_data.prompt_confirm = false;
|
||||||
if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action {
|
if let Some(radarr_event) = &self.data.radarr_data.prompt_confirm_action {
|
||||||
self.dispatch_network_event((*radarr_event).into()).await;
|
self
|
||||||
|
.dispatch_network_event(radarr_event.clone().into())
|
||||||
|
.await;
|
||||||
self.should_refresh = true;
|
self.should_refresh = true;
|
||||||
self.data.radarr_data.prompt_confirm_action = None;
|
self.data.radarr_data.prompt_confirm_action = None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_dispatch_by_collection_details_block_with_add_movie() {
|
async fn test_dispatch_by_collection_details_block_with_add_movie() {
|
||||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||||
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie);
|
app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
|
||||||
|
|
||||||
app.data.radarr_data.collections.set_items(vec![Collection {
|
app.data.radarr_data.collections.set_items(vec![Collection {
|
||||||
movies: Some(vec![CollectionMovie::default()]),
|
movies: Some(vec![CollectionMovie::default()]),
|
||||||
@@ -82,7 +82,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::AddMovie.into()
|
RadarrEvent::AddMovie(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
assert!(!app.data.radarr_data.collection_movies.items.is_empty());
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -162,17 +162,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_dispatch_by_indexer_settings_block() {
|
async fn test_dispatch_by_all_indexer_settings_block() {
|
||||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||||
|
|
||||||
app
|
app
|
||||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::IndexerSettingsPrompt)
|
.dispatch_by_radarr_block(&ActiveRadarrBlock::AllIndexerSettingsPrompt)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetIndexerSettings.into()
|
RadarrEvent::GetAllIndexerSettings.into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -189,7 +189,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::TestIndexer.into()
|
RadarrEvent::TestIndexer(None).into()
|
||||||
);
|
);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
}
|
}
|
||||||
@@ -229,7 +229,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetLogs.into()
|
RadarrEvent::GetLogs(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -263,7 +263,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::SearchNewMovie.into()
|
RadarrEvent::SearchNewMovie(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -280,7 +280,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieDetails.into()
|
RadarrEvent::GetMovieDetails(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -297,7 +297,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieDetails.into()
|
RadarrEvent::GetMovieDetails(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -314,7 +314,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieHistory.into()
|
RadarrEvent::GetMovieHistory(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -331,7 +331,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieCredits.into()
|
RadarrEvent::GetMovieCredits(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -354,7 +354,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieCredits.into()
|
RadarrEvent::GetMovieCredits(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -377,7 +377,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetMovieCredits.into()
|
RadarrEvent::GetMovieCredits(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
@@ -418,7 +418,7 @@ mod tests {
|
|||||||
assert!(app.is_loading);
|
assert!(app.is_loading);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sync_network_rx.recv().await.unwrap(),
|
sync_network_rx.recv().await.unwrap(),
|
||||||
RadarrEvent::GetReleases.into()
|
RadarrEvent::GetReleases(None).into()
|
||||||
);
|
);
|
||||||
assert!(!app.data.radarr_data.prompt_confirm);
|
assert!(!app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use clap::{error::ErrorKind, CommandFactory};
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{handle_command, mutex_flags_or_option, radarr::RadarrCommand},
|
||||||
|
models::{
|
||||||
|
radarr_models::{BlocklistItem, BlocklistResponse, RadarrSerdeable},
|
||||||
|
Serdeable,
|
||||||
|
},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
Cli,
|
||||||
|
};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_subcommand_requires_subcommand() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_subcommand_delegates_to_radarr() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "all-indexer-settings"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completions_requires_argument() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "completions"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completions_invalid_argument() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "completions", "test"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_completions_satisfied_with_argument() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "completions", "bash"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(false, false, None)]
|
||||||
|
#[case(false, true, Some(false))]
|
||||||
|
#[case(true, false, Some(true))]
|
||||||
|
fn test_mutex_flags_or_option(
|
||||||
|
#[case] positive: bool,
|
||||||
|
#[case] negative: bool,
|
||||||
|
#[case] expected_output: Option<bool>,
|
||||||
|
) {
|
||||||
|
let result = mutex_flags_or_option(positive, negative);
|
||||||
|
|
||||||
|
assert_eq!(result, expected_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(false, false, true, true)]
|
||||||
|
#[case(false, false, false, false)]
|
||||||
|
#[case(false, true, true, false)]
|
||||||
|
#[case(true, false, false, true)]
|
||||||
|
fn test_mutex_flags_or_default(
|
||||||
|
#[case] positive: bool,
|
||||||
|
#[case] negative: bool,
|
||||||
|
#[case] default_value: bool,
|
||||||
|
#[case] expected_output: bool,
|
||||||
|
) {
|
||||||
|
use crate::cli::mutex_flags_or_default;
|
||||||
|
|
||||||
|
let result = mutex_flags_or_default(positive, negative, default_value);
|
||||||
|
|
||||||
|
assert_eq!(result, expected_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cli_handler_delegates_radarr_commands_to_the_radarr_cli_handler() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::GetBlocklist.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::BlocklistResponse(
|
||||||
|
BlocklistResponse {
|
||||||
|
records: vec![BlocklistItem::default()],
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::ClearBlocklist.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let claer_blocklist_command = RadarrCommand::ClearBlocklist.into();
|
||||||
|
|
||||||
|
let result = handle_command(&app_arc, claer_blocklist_command, &mut mock_network).await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{command, Subcommand};
|
||||||
|
use clap_complete::Shell;
|
||||||
|
use radarr::{RadarrCliHandler, RadarrCommand};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{app::App, network::NetworkTrait};
|
||||||
|
|
||||||
|
pub mod radarr;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "cli_tests.rs"]
|
||||||
|
mod cli_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Subcommand, PartialEq, Eq)]
|
||||||
|
pub enum Command {
|
||||||
|
#[command(subcommand, about = "Commands for manging your Radarr instance")]
|
||||||
|
Radarr(RadarrCommand),
|
||||||
|
|
||||||
|
#[command(
|
||||||
|
arg_required_else_help = true,
|
||||||
|
about = "Generate shell completions for the Managarr CLI"
|
||||||
|
)]
|
||||||
|
Completions {
|
||||||
|
#[arg(value_enum)]
|
||||||
|
shell: Shell,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CliCommandHandler<'a, 'b, T: Into<Command>> {
|
||||||
|
fn with(app: &'a Arc<Mutex<App<'b>>>, command: T, network: &'a mut dyn NetworkTrait) -> Self;
|
||||||
|
async fn handle(self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn handle_command(
|
||||||
|
app: &Arc<Mutex<App<'_>>>,
|
||||||
|
command: Command,
|
||||||
|
network: &mut dyn NetworkTrait,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Command::Radarr(radarr_command) = command {
|
||||||
|
RadarrCliHandler::with(app, radarr_command, network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn mutex_flags_or_option(positive: bool, negative: bool) -> Option<bool> {
|
||||||
|
if positive {
|
||||||
|
Some(true)
|
||||||
|
} else if negative {
|
||||||
|
Some(false)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn mutex_flags_or_default(positive: bool, negative: bool, default_value: bool) -> bool {
|
||||||
|
if positive {
|
||||||
|
true
|
||||||
|
} else if negative {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
default_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! execute_network_event {
|
||||||
|
($self:ident, $event:expr) => {
|
||||||
|
let resp = $self.network.handle_network_event($event.into()).await?;
|
||||||
|
let json = serde_json::to_string_pretty(&resp)?;
|
||||||
|
println!("{}", json);
|
||||||
|
};
|
||||||
|
($self:ident, $event:expr, $happy_output:expr) => {
|
||||||
|
$self.network.handle_network_event($event.into()).await?;
|
||||||
|
println!("{}", $happy_output);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{arg, command, ArgAction, Subcommand};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
models::radarr_models::{AddMovieBody, AddOptions, MinimumAvailability, Monitor},
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "add_command_handler_tests.rs"]
|
||||||
|
mod add_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrAddCommand {
|
||||||
|
#[command(about = "Add a new movie to your Radarr library")]
|
||||||
|
Movie {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The TMDB ID of the film you wish to add to your library",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
tmdb_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The root folder path where all film data and metadata should live",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
root_folder_path: String,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the quality profile to use for this movie",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
quality_profile_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The minimum availability to monitor for this film",
|
||||||
|
value_enum,
|
||||||
|
default_value_t = MinimumAvailability::default()
|
||||||
|
)]
|
||||||
|
minimum_availability: MinimumAvailability,
|
||||||
|
#[arg(long, help = "Should Radarr monitor this film")]
|
||||||
|
disable_monitoring: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Tag IDs to tag the film with",
|
||||||
|
value_parser,
|
||||||
|
action = ArgAction::Append
|
||||||
|
)]
|
||||||
|
tag: Vec<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "What Radarr should monitor",
|
||||||
|
value_enum,
|
||||||
|
default_value_t = Monitor::default()
|
||||||
|
)]
|
||||||
|
monitor: Monitor,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Tell Radarr to not start a search for this film once it's added to your library",
|
||||||
|
)]
|
||||||
|
no_search_for_movie: bool,
|
||||||
|
},
|
||||||
|
#[command(about = "Add a new root folder")]
|
||||||
|
RootFolder {
|
||||||
|
#[arg(long, help = "The path of the new root folder", required = true)]
|
||||||
|
root_folder_path: String,
|
||||||
|
},
|
||||||
|
#[command(about = "Add new tag")]
|
||||||
|
Tag {
|
||||||
|
#[arg(long, help = "The name of the tag to be added", required = true)]
|
||||||
|
name: String
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrAddCommand> for Command {
|
||||||
|
fn from(value: RadarrAddCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::Add(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrAddCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrAddCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHandler<'a, 'b> {
|
||||||
|
fn with(_app: &'a Arc<Mutex<App<'b>>>, command: RadarrAddCommand, network: &'a mut dyn NetworkTrait) -> Self {
|
||||||
|
RadarrAddCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrAddCommand::Movie {
|
||||||
|
tmdb_id,
|
||||||
|
root_folder_path,
|
||||||
|
quality_profile_id,
|
||||||
|
minimum_availability,
|
||||||
|
disable_monitoring,
|
||||||
|
tag: tags,
|
||||||
|
monitor,
|
||||||
|
no_search_for_movie,
|
||||||
|
} => {
|
||||||
|
let body = AddMovieBody {
|
||||||
|
tmdb_id,
|
||||||
|
title: String::new(),
|
||||||
|
root_folder_path,
|
||||||
|
quality_profile_id,
|
||||||
|
minimum_availability: minimum_availability.to_string(),
|
||||||
|
monitored: !disable_monitoring,
|
||||||
|
tags,
|
||||||
|
add_options: AddOptions {
|
||||||
|
monitor: monitor.to_string(),
|
||||||
|
search_for_movie: !no_search_for_movie,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
execute_network_event!(self, RadarrEvent::AddMovie(Some(body)));
|
||||||
|
}
|
||||||
|
RadarrAddCommand::RootFolder { root_folder_path } => {
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::AddRootFolder(Some(root_folder_path.clone()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RadarrAddCommand::Tag { name } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::AddTag(name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,472 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use clap::{error::ErrorKind, CommandFactory, Parser};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cli::{
|
||||||
|
radarr::{add_command_handler::RadarrAddCommand, RadarrCommand},
|
||||||
|
Command,
|
||||||
|
},
|
||||||
|
models::radarr_models::{MinimumAvailability, Monitor},
|
||||||
|
Cli,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_add_command_from() {
|
||||||
|
let command = RadarrAddCommand::Tag {
|
||||||
|
name: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(RadarrCommand::Add(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_requires_arguments() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "add", "movie"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_requires_root_folder_path() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_requires_quality_profile_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_requires_tmdb_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_add_movie_assert_argument_flags_require_args(
|
||||||
|
#[values("--minimum-availability", "--tag", "--monitor")] flag: &str,
|
||||||
|
) {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
flag,
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_all_arguments_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_minimum_availability_validation() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--minimum-availability",
|
||||||
|
"test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_monitor_validation() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--monitor",
|
||||||
|
"test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_defaults() {
|
||||||
|
let expected_args = RadarrAddCommand::Movie {
|
||||||
|
tmdb_id: 1,
|
||||||
|
root_folder_path: "/test".to_owned(),
|
||||||
|
quality_profile_id: 1,
|
||||||
|
minimum_availability: MinimumAvailability::default(),
|
||||||
|
disable_monitoring: false,
|
||||||
|
tag: vec![],
|
||||||
|
monitor: Monitor::default(),
|
||||||
|
no_search_for_movie: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Add(add_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(add_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_tags_is_repeatable() {
|
||||||
|
let expected_args = RadarrAddCommand::Movie {
|
||||||
|
tmdb_id: 1,
|
||||||
|
root_folder_path: "/test".to_owned(),
|
||||||
|
quality_profile_id: 1,
|
||||||
|
minimum_availability: MinimumAvailability::default(),
|
||||||
|
disable_monitoring: false,
|
||||||
|
tag: vec![1, 2],
|
||||||
|
monitor: Monitor::default(),
|
||||||
|
no_search_for_movie: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--tag",
|
||||||
|
"1",
|
||||||
|
"--tag",
|
||||||
|
"2",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Add(add_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(add_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_movie_all_args_defined() {
|
||||||
|
let expected_args = RadarrAddCommand::Movie {
|
||||||
|
tmdb_id: 1,
|
||||||
|
root_folder_path: "/test".to_owned(),
|
||||||
|
quality_profile_id: 1,
|
||||||
|
minimum_availability: MinimumAvailability::Released,
|
||||||
|
disable_monitoring: true,
|
||||||
|
tag: vec![1, 2],
|
||||||
|
monitor: Monitor::MovieAndCollection,
|
||||||
|
no_search_for_movie: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"movie",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/test",
|
||||||
|
"--quality-profile-id",
|
||||||
|
"1",
|
||||||
|
"--minimum-availability",
|
||||||
|
"released",
|
||||||
|
"--disable-monitoring",
|
||||||
|
"--tmdb-id",
|
||||||
|
"1",
|
||||||
|
"--tag",
|
||||||
|
"1",
|
||||||
|
"--tag",
|
||||||
|
"2",
|
||||||
|
"--monitor",
|
||||||
|
"movie-and-collection",
|
||||||
|
"--no-search-for-movie",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Add(add_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(add_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_root_folder_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "add", "root-folder"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_root_folder_success() {
|
||||||
|
let expected_args = RadarrAddCommand::RootFolder {
|
||||||
|
root_folder_path: "/nfs/test".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"add",
|
||||||
|
"root-folder",
|
||||||
|
"--root-folder-path",
|
||||||
|
"/nfs/test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Add(add_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(add_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_tag_requires_arguments() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "add", "tag"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_tag_success() {
|
||||||
|
let expected_args = RadarrAddCommand::Tag {
|
||||||
|
name: "test".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from(["managarr", "radarr", "add", "tag", "--name", "test"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Add(add_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(add_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{radarr::add_command_handler::RadarrAddCommandHandler, CliCommandHandler},
|
||||||
|
models::{
|
||||||
|
radarr_models::{AddMovieBody, AddOptions, RadarrSerdeable},
|
||||||
|
Serdeable,
|
||||||
|
},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_add_movie_command() {
|
||||||
|
let expected_add_movie_body = AddMovieBody {
|
||||||
|
tmdb_id: 1,
|
||||||
|
title: String::new(),
|
||||||
|
root_folder_path: "/test".to_owned(),
|
||||||
|
quality_profile_id: 1,
|
||||||
|
minimum_availability: "released".to_owned(),
|
||||||
|
monitored: false,
|
||||||
|
tags: vec![1, 2],
|
||||||
|
add_options: AddOptions {
|
||||||
|
monitor: "movieAndCollection".to_owned(),
|
||||||
|
search_for_movie: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::AddMovie(Some(expected_add_movie_body)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let add_movie_command = RadarrAddCommand::Movie {
|
||||||
|
tmdb_id: 1,
|
||||||
|
root_folder_path: "/test".to_owned(),
|
||||||
|
quality_profile_id: 1,
|
||||||
|
minimum_availability: MinimumAvailability::Released,
|
||||||
|
disable_monitoring: true,
|
||||||
|
tag: vec![1, 2],
|
||||||
|
monitor: Monitor::MovieAndCollection,
|
||||||
|
no_search_for_movie: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrAddCommandHandler::with(&app_arc, add_movie_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_add_root_folder_command() {
|
||||||
|
let expected_root_folder_path = "/nfs/test".to_owned();
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::AddRootFolder(Some(expected_root_folder_path.clone())).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let add_root_folder_command = RadarrAddCommand::RootFolder {
|
||||||
|
root_folder_path: expected_root_folder_path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrAddCommandHandler::with(&app_arc, add_root_folder_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_add_tag_command() {
|
||||||
|
let expected_tag_name = "test".to_owned();
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::AddTag(expected_tag_name.clone()).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let add_tag_command = RadarrAddCommand::Tag {
|
||||||
|
name: expected_tag_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrAddCommandHandler::with(&app_arc, add_tag_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
models::radarr_models::DeleteMovieParams,
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "delete_command_handler_tests.rs"]
|
||||||
|
mod delete_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrDeleteCommand {
|
||||||
|
#[command(about = "Delete the specified item from the Radarr blocklist")]
|
||||||
|
BlocklistItem {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the blocklist item to remove from the blocklist",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
blocklist_item_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Delete the specified download")]
|
||||||
|
Download {
|
||||||
|
#[arg(long, help = "The ID of the download to delete", required = true)]
|
||||||
|
download_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Delete the indexer with the given ID")]
|
||||||
|
Indexer {
|
||||||
|
#[arg(long, help = "The ID of the indexer to delete", required = true)]
|
||||||
|
indexer_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Delete a movie from your Radarr library")]
|
||||||
|
Movie {
|
||||||
|
#[arg(long, help = "The ID of the movie to delete", required = true)]
|
||||||
|
movie_id: i64,
|
||||||
|
#[arg(long, help = "Delete the movie files from disk as well")]
|
||||||
|
delete_files_from_disk: bool,
|
||||||
|
#[arg(long, help = "Add a list exclusion for this film")]
|
||||||
|
add_list_exclusion: bool,
|
||||||
|
},
|
||||||
|
#[command(about = "Delete the root folder with the given ID")]
|
||||||
|
RootFolder {
|
||||||
|
#[arg(long, help = "The ID of the root folder to delete", required = true)]
|
||||||
|
root_folder_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Delete the tag with the specified ID")]
|
||||||
|
Tag {
|
||||||
|
#[arg(long, help = "The ID of the tag to delete", required = true)]
|
||||||
|
tag_id: i64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrDeleteCommand> for Command {
|
||||||
|
fn from(value: RadarrDeleteCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::Delete(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrDeleteCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrDeleteCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrDeleteCommand> for RadarrDeleteCommandHandler<'a, 'b> {
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrDeleteCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrDeleteCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrDeleteCommand::BlocklistItem { blocklist_item_id } => {
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::DeleteBlocklistItem(Some(blocklist_item_id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RadarrDeleteCommand::Download { download_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::DeleteDownload(Some(download_id)));
|
||||||
|
}
|
||||||
|
RadarrDeleteCommand::Indexer { indexer_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::DeleteIndexer(Some(indexer_id)));
|
||||||
|
}
|
||||||
|
RadarrDeleteCommand::Movie {
|
||||||
|
movie_id,
|
||||||
|
delete_files_from_disk,
|
||||||
|
add_list_exclusion,
|
||||||
|
} => {
|
||||||
|
let delete_movie_params = DeleteMovieParams {
|
||||||
|
id: movie_id,
|
||||||
|
delete_movie_files: delete_files_from_disk,
|
||||||
|
add_list_exclusion,
|
||||||
|
};
|
||||||
|
execute_network_event!(self, RadarrEvent::DeleteMovie(Some(delete_movie_params)));
|
||||||
|
}
|
||||||
|
RadarrDeleteCommand::RootFolder { root_folder_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::DeleteRootFolder(Some(root_folder_id)));
|
||||||
|
}
|
||||||
|
RadarrDeleteCommand::Tag { tag_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::DeleteTag(tag_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,432 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{
|
||||||
|
cli::{
|
||||||
|
radarr::{delete_command_handler::RadarrDeleteCommand, RadarrCommand},
|
||||||
|
Command,
|
||||||
|
},
|
||||||
|
Cli,
|
||||||
|
};
|
||||||
|
use clap::{error::ErrorKind, CommandFactory, Parser};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_delete_command_from() {
|
||||||
|
let command = RadarrDeleteCommand::BlocklistItem {
|
||||||
|
blocklist_item_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(RadarrCommand::Delete(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_blocklist_item_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "blocklist-item"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_blocklist_item_success() {
|
||||||
|
let expected_args = RadarrDeleteCommand::BlocklistItem {
|
||||||
|
blocklist_item_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"delete",
|
||||||
|
"blocklist-item",
|
||||||
|
"--blocklist-item-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_download_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "download"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_download_success() {
|
||||||
|
let expected_args = RadarrDeleteCommand::Download { download_id: 1 };
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"delete",
|
||||||
|
"download",
|
||||||
|
"--download-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_indexer_requires_arguments() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "indexer"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_indexer_success() {
|
||||||
|
let expected_args = RadarrDeleteCommand::Indexer { indexer_id: 1 };
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"delete",
|
||||||
|
"indexer",
|
||||||
|
"--indexer-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_movie_requires_arguments() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "movie"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_movie_defaults() {
|
||||||
|
let expected_args = RadarrDeleteCommand::Movie {
|
||||||
|
movie_id: 1,
|
||||||
|
delete_files_from_disk: false,
|
||||||
|
add_list_exclusion: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
Cli::try_parse_from(["managarr", "radarr", "delete", "movie", "--movie-id", "1"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_movie_all_args_defined() {
|
||||||
|
let expected_args = RadarrDeleteCommand::Movie {
|
||||||
|
movie_id: 1,
|
||||||
|
delete_files_from_disk: true,
|
||||||
|
add_list_exclusion: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"delete",
|
||||||
|
"movie",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
"--delete-files-from-disk",
|
||||||
|
"--add-list-exclusion",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_root_folder_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "root-folder"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_root_folder_success() {
|
||||||
|
let expected_args = RadarrDeleteCommand::RootFolder { root_folder_id: 1 };
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"delete",
|
||||||
|
"root-folder",
|
||||||
|
"--root-folder-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_tag_requires_arguments() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "delete", "tag"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_tag_success() {
|
||||||
|
let expected_args = RadarrDeleteCommand::Tag { tag_id: 1 };
|
||||||
|
|
||||||
|
let result = Cli::try_parse_from(["managarr", "radarr", "delete", "tag", "--tag-id", "1"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(delete_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{
|
||||||
|
radarr::delete_command_handler::{RadarrDeleteCommand, RadarrDeleteCommandHandler},
|
||||||
|
CliCommandHandler,
|
||||||
|
},
|
||||||
|
models::{
|
||||||
|
radarr_models::{DeleteMovieParams, RadarrSerdeable},
|
||||||
|
Serdeable,
|
||||||
|
},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_blocklist_item_command() {
|
||||||
|
let expected_blocklist_item_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_blocklist_item_command = RadarrDeleteCommand::BlocklistItem {
|
||||||
|
blocklist_item_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrDeleteCommandHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
delete_blocklist_item_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_download_command() {
|
||||||
|
let expected_download_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteDownload(Some(expected_download_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_download_command = RadarrDeleteCommand::Download { download_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrDeleteCommandHandler::with(&app_arc, delete_download_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_indexer_command() {
|
||||||
|
let expected_indexer_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteIndexer(Some(expected_indexer_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_indexer_command = RadarrDeleteCommand::Indexer { indexer_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrDeleteCommandHandler::with(&app_arc, delete_indexer_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_movie_command() {
|
||||||
|
let expected_delete_movie_params = DeleteMovieParams {
|
||||||
|
id: 1,
|
||||||
|
delete_movie_files: true,
|
||||||
|
add_list_exclusion: true,
|
||||||
|
};
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteMovie(Some(expected_delete_movie_params)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_movie_command = RadarrDeleteCommand::Movie {
|
||||||
|
movie_id: 1,
|
||||||
|
delete_files_from_disk: true,
|
||||||
|
add_list_exclusion: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrDeleteCommandHandler::with(&app_arc, delete_movie_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_root_folder_command() {
|
||||||
|
let expected_root_folder_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteRootFolder(Some(expected_root_folder_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_root_folder_command = RadarrDeleteCommand::RootFolder { root_folder_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrDeleteCommandHandler::with(&app_arc, delete_root_folder_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_delete_tag_command() {
|
||||||
|
let expected_tag_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteTag(expected_tag_id).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_tag_command = RadarrDeleteCommand::Tag { tag_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrDeleteCommandHandler::with(&app_arc, delete_tag_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,498 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{ArgAction, ArgGroup, Subcommand};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{mutex_flags_or_default, mutex_flags_or_option, CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
models::{
|
||||||
|
radarr_models::{
|
||||||
|
EditCollectionParams, EditIndexerParams, EditMovieParams, IndexerSettings,
|
||||||
|
MinimumAvailability, RadarrSerdeable,
|
||||||
|
},
|
||||||
|
Serdeable,
|
||||||
|
},
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "edit_command_handler_tests.rs"]
|
||||||
|
mod edit_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrEditCommand {
|
||||||
|
#[command(
|
||||||
|
about = "Edit and indexer settings that apply to all indexers",
|
||||||
|
group(
|
||||||
|
ArgGroup::new("edit_settings")
|
||||||
|
.args([
|
||||||
|
"allow_hardcoded_subs",
|
||||||
|
"disable_allow_hardcoded_subs",
|
||||||
|
"availability_delay",
|
||||||
|
"maximum_size",
|
||||||
|
"minimum_age",
|
||||||
|
"prefer_indexer_flags",
|
||||||
|
"disable_prefer_indexer_flags",
|
||||||
|
"retention",
|
||||||
|
"rss_sync_interval",
|
||||||
|
"whitelisted_subtitle_tags"
|
||||||
|
]).required(true)
|
||||||
|
.multiple(true))
|
||||||
|
)]
|
||||||
|
AllIndexerSettings {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Detected hardcoded subs will be automatically downloaded",
|
||||||
|
conflicts_with = "disable_allow_hardcoded_subs"
|
||||||
|
)]
|
||||||
|
allow_hardcoded_subs: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable allowing detected hardcoded subs from being automatically downloaded",
|
||||||
|
conflicts_with = "allow_hardcoded_subs"
|
||||||
|
)]
|
||||||
|
disable_allow_hardcoded_subs: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Amount of time in days before or after available date to search for Movie"
|
||||||
|
)]
|
||||||
|
availability_delay: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The maximum size for a release to be grabbed in MB. Set to zero to set to unlimited"
|
||||||
|
)]
|
||||||
|
maximum_size: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Usenet only: Minimum age in minutes of NZBs before they are grabbed. Use this to give new releases time to propagate to your usenet provider."
|
||||||
|
)]
|
||||||
|
minimum_age: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Prioritize releases with special flags",
|
||||||
|
conflicts_with = "disable_prefer_indexer_flags"
|
||||||
|
)]
|
||||||
|
prefer_indexer_flags: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable prioritizing releases with special flags",
|
||||||
|
conflicts_with = "prefer_indexer_flags"
|
||||||
|
)]
|
||||||
|
disable_prefer_indexer_flags: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Usenet only: The retention time in days to retain releases. Set to zero to set for unlimited retention"
|
||||||
|
)]
|
||||||
|
retention: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The RSS sync interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)"
|
||||||
|
)]
|
||||||
|
rss_sync_interval: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "A comma separated list of subtitle tags that will not be considered hardcoded"
|
||||||
|
)]
|
||||||
|
whitelisted_subtitle_tags: Option<String>,
|
||||||
|
},
|
||||||
|
#[command(
|
||||||
|
about = "Edit preferences for the specified collection",
|
||||||
|
group(
|
||||||
|
ArgGroup::new("edit_collection")
|
||||||
|
.args([
|
||||||
|
"enable_monitoring",
|
||||||
|
"disable_monitoring",
|
||||||
|
"minimum_availability",
|
||||||
|
"quality_profile_id",
|
||||||
|
"root_folder_path",
|
||||||
|
"search_on_add",
|
||||||
|
"disable_search_on_add"
|
||||||
|
]).required(true)
|
||||||
|
.multiple(true))
|
||||||
|
)]
|
||||||
|
Collection {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the collection whose preferences you want to edit",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
collection_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Monitor to automatically have movies from this collection added to your library",
|
||||||
|
conflicts_with = "disable_monitoring"
|
||||||
|
)]
|
||||||
|
enable_monitoring: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable monitoring for this collection so movies from this collection are not automatically added to your library",
|
||||||
|
conflicts_with = "enable_monitoring"
|
||||||
|
)]
|
||||||
|
disable_monitoring: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Specify the minimum availability for all movies in this collection",
|
||||||
|
value_enum
|
||||||
|
)]
|
||||||
|
minimum_availability: Option<MinimumAvailability>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the quality profile that all movies in this collection should use"
|
||||||
|
)]
|
||||||
|
quality_profile_id: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The root folder path that all movies in this collection should exist under"
|
||||||
|
)]
|
||||||
|
root_folder_path: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Search for movies from this collection when added to your library",
|
||||||
|
conflicts_with = "disable_search_on_add"
|
||||||
|
)]
|
||||||
|
search_on_add: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable triggering searching whenever new movies are added to this collection",
|
||||||
|
conflicts_with = "search_on_add"
|
||||||
|
)]
|
||||||
|
disable_search_on_add: bool,
|
||||||
|
},
|
||||||
|
#[command(
|
||||||
|
about = "Edit preferences for the specified indexer",
|
||||||
|
group(
|
||||||
|
ArgGroup::new("edit_indexer")
|
||||||
|
.args([
|
||||||
|
"name",
|
||||||
|
"enable_rss",
|
||||||
|
"disable_rss",
|
||||||
|
"enable_automatic_search",
|
||||||
|
"disable_automatic_search",
|
||||||
|
"enable_interactive_search",
|
||||||
|
"disable_automatic_search",
|
||||||
|
"url",
|
||||||
|
"api_key",
|
||||||
|
"seed_ratio",
|
||||||
|
"tag",
|
||||||
|
"priority",
|
||||||
|
"clear_tags"
|
||||||
|
]).required(true)
|
||||||
|
.multiple(true))
|
||||||
|
)]
|
||||||
|
Indexer {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the indexer whose settings you wish to edit",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
indexer_id: i64,
|
||||||
|
#[arg(long, help = "The name of the indexer")]
|
||||||
|
name: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Indicate to Radarr that this indexer should be used when Radarr periodically looks for releases via RSS Sync",
|
||||||
|
conflicts_with = "disable_rss"
|
||||||
|
)]
|
||||||
|
enable_rss: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable using this indexer when Radarr periodically looks for releases via RSS Sync",
|
||||||
|
conflicts_with = "enable_rss"
|
||||||
|
)]
|
||||||
|
disable_rss: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Indicate to Radarr that this indexer should be used when automatic searches are performed via the UI or by Radarr",
|
||||||
|
conflicts_with = "disable_automatic_search"
|
||||||
|
)]
|
||||||
|
enable_automatic_search: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable using this indexer whenever automatic searches are performed via the UI or by Radarr",
|
||||||
|
conflicts_with = "enable_automatic_search"
|
||||||
|
)]
|
||||||
|
disable_automatic_search: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Indicate to Radarr that this indexer should be used when an interactive search is used",
|
||||||
|
conflicts_with = "disable_interactive_search"
|
||||||
|
)]
|
||||||
|
enable_interactive_search: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable using this indexer whenever an interactive search is performed",
|
||||||
|
conflicts_with = "enable_interactive_search"
|
||||||
|
)]
|
||||||
|
disable_interactive_search: bool,
|
||||||
|
#[arg(long, help = "The URL of the indexer")]
|
||||||
|
url: Option<String>,
|
||||||
|
#[arg(long, help = "The API key used to access the indexer's API")]
|
||||||
|
api_key: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ratio a torrent should reach before stopping; Empty uses the download client's default. Ratio should be at least 1.0 and follow the indexer's rules"
|
||||||
|
)]
|
||||||
|
seed_ratio: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Only use this indexer for movies with at least one matching tag ID. Leave blank to use with all movies.",
|
||||||
|
value_parser,
|
||||||
|
action = ArgAction::Append,
|
||||||
|
conflicts_with = "clear_tags"
|
||||||
|
)]
|
||||||
|
tag: Option<Vec<i64>>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Indexer Priority from 1 (Highest) to 50 (Lowest). Default: 25. Used when grabbing releases as a tiebreaker for otherwise equal releases, Radarr will still use all enabled indexers for RSS Sync and Searching"
|
||||||
|
)]
|
||||||
|
priority: Option<i64>,
|
||||||
|
#[arg(long, help = "Clear all tags on this indexer", conflicts_with = "tag")]
|
||||||
|
clear_tags: bool,
|
||||||
|
},
|
||||||
|
#[command(
|
||||||
|
about = "Edit preferences for the specified movie",
|
||||||
|
group(
|
||||||
|
ArgGroup::new("edit_movie")
|
||||||
|
.args([
|
||||||
|
"enable_monitoring",
|
||||||
|
"disable_monitoring",
|
||||||
|
"minimum_availability",
|
||||||
|
"quality_profile_id",
|
||||||
|
"root_folder_path",
|
||||||
|
"tag",
|
||||||
|
"clear_tags"
|
||||||
|
]).required(true)
|
||||||
|
.multiple(true))
|
||||||
|
)]
|
||||||
|
Movie {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the movie whose settings you want to edit",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Enable monitoring of this movie in Radarr so Radarr will automatically download this movie if it is available",
|
||||||
|
conflicts_with = "disable_monitoring"
|
||||||
|
)]
|
||||||
|
enable_monitoring: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Disable monitoring of this movie so Radarr does not automatically download the movie if it is found to be available",
|
||||||
|
conflicts_with = "enable_monitoring"
|
||||||
|
)]
|
||||||
|
disable_monitoring: bool,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The minimum availability to monitor for this film",
|
||||||
|
value_enum
|
||||||
|
)]
|
||||||
|
minimum_availability: Option<MinimumAvailability>,
|
||||||
|
#[arg(long, help = "The ID of the quality profile to use for this movie")]
|
||||||
|
quality_profile_id: Option<i64>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The root folder path where all film data and metadata should live"
|
||||||
|
)]
|
||||||
|
root_folder_path: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Tag IDs to tag this movie with",
|
||||||
|
value_parser,
|
||||||
|
action = ArgAction::Append,
|
||||||
|
conflicts_with = "clear_tags"
|
||||||
|
)]
|
||||||
|
tag: Option<Vec<i64>>,
|
||||||
|
#[arg(long, help = "Clear all tags on this movie", conflicts_with = "tag")]
|
||||||
|
clear_tags: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrEditCommand> for Command {
|
||||||
|
fn from(value: RadarrEditCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::Edit(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrEditCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrEditCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandHandler<'a, 'b> {
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrEditCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrEditCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrEditCommand::AllIndexerSettings {
|
||||||
|
allow_hardcoded_subs,
|
||||||
|
disable_allow_hardcoded_subs,
|
||||||
|
availability_delay,
|
||||||
|
maximum_size,
|
||||||
|
minimum_age,
|
||||||
|
prefer_indexer_flags,
|
||||||
|
disable_prefer_indexer_flags,
|
||||||
|
retention,
|
||||||
|
rss_sync_interval,
|
||||||
|
whitelisted_subtitle_tags,
|
||||||
|
} => {
|
||||||
|
if let Serdeable::Radarr(RadarrSerdeable::IndexerSettings(previous_indexer_settings)) = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(RadarrEvent::GetAllIndexerSettings.into())
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
let allow_hardcoded_subs_value = mutex_flags_or_default(
|
||||||
|
allow_hardcoded_subs,
|
||||||
|
disable_allow_hardcoded_subs,
|
||||||
|
previous_indexer_settings.allow_hardcoded_subs,
|
||||||
|
);
|
||||||
|
let prefer_indexer_flags_value = mutex_flags_or_default(
|
||||||
|
prefer_indexer_flags,
|
||||||
|
disable_prefer_indexer_flags,
|
||||||
|
previous_indexer_settings.prefer_indexer_flags,
|
||||||
|
);
|
||||||
|
let params = IndexerSettings {
|
||||||
|
id: 1,
|
||||||
|
allow_hardcoded_subs: allow_hardcoded_subs_value,
|
||||||
|
availability_delay: availability_delay
|
||||||
|
.unwrap_or(previous_indexer_settings.availability_delay),
|
||||||
|
maximum_size: maximum_size.unwrap_or(previous_indexer_settings.maximum_size),
|
||||||
|
minimum_age: minimum_age.unwrap_or(previous_indexer_settings.minimum_age),
|
||||||
|
prefer_indexer_flags: prefer_indexer_flags_value,
|
||||||
|
retention: retention.unwrap_or(previous_indexer_settings.retention),
|
||||||
|
rss_sync_interval: rss_sync_interval
|
||||||
|
.unwrap_or(previous_indexer_settings.rss_sync_interval),
|
||||||
|
whitelisted_hardcoded_subs: whitelisted_subtitle_tags
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
previous_indexer_settings
|
||||||
|
.whitelisted_hardcoded_subs
|
||||||
|
.text
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::EditAllIndexerSettings(Some(params)),
|
||||||
|
"All indexer settings updated"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RadarrEditCommand::Collection {
|
||||||
|
collection_id,
|
||||||
|
enable_monitoring,
|
||||||
|
disable_monitoring,
|
||||||
|
minimum_availability,
|
||||||
|
quality_profile_id,
|
||||||
|
root_folder_path,
|
||||||
|
search_on_add,
|
||||||
|
disable_search_on_add,
|
||||||
|
} => {
|
||||||
|
let monitored_value = mutex_flags_or_option(enable_monitoring, disable_monitoring);
|
||||||
|
let search_on_add_value = mutex_flags_or_option(search_on_add, disable_search_on_add);
|
||||||
|
|
||||||
|
let edit_collection_params = EditCollectionParams {
|
||||||
|
collection_id,
|
||||||
|
monitored: monitored_value,
|
||||||
|
minimum_availability,
|
||||||
|
quality_profile_id,
|
||||||
|
root_folder_path,
|
||||||
|
search_on_add: search_on_add_value,
|
||||||
|
};
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::EditCollection(Some(edit_collection_params)),
|
||||||
|
"Collection Updated"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RadarrEditCommand::Indexer {
|
||||||
|
indexer_id,
|
||||||
|
name,
|
||||||
|
enable_rss,
|
||||||
|
disable_rss,
|
||||||
|
enable_automatic_search,
|
||||||
|
disable_automatic_search,
|
||||||
|
enable_interactive_search,
|
||||||
|
disable_interactive_search,
|
||||||
|
url,
|
||||||
|
api_key,
|
||||||
|
seed_ratio,
|
||||||
|
tag,
|
||||||
|
priority,
|
||||||
|
clear_tags,
|
||||||
|
} => {
|
||||||
|
let rss_value = mutex_flags_or_option(enable_rss, disable_rss);
|
||||||
|
let automatic_search_value =
|
||||||
|
mutex_flags_or_option(enable_automatic_search, disable_automatic_search);
|
||||||
|
let interactive_search_value =
|
||||||
|
mutex_flags_or_option(enable_interactive_search, disable_interactive_search);
|
||||||
|
let edit_indexer_params = EditIndexerParams {
|
||||||
|
indexer_id,
|
||||||
|
name,
|
||||||
|
enable_rss: rss_value,
|
||||||
|
enable_automatic_search: automatic_search_value,
|
||||||
|
enable_interactive_search: interactive_search_value,
|
||||||
|
url,
|
||||||
|
api_key,
|
||||||
|
seed_ratio,
|
||||||
|
tags: tag,
|
||||||
|
priority,
|
||||||
|
clear_tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::EditIndexer(Some(edit_indexer_params)),
|
||||||
|
"Indexer updated"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RadarrEditCommand::Movie {
|
||||||
|
movie_id,
|
||||||
|
enable_monitoring,
|
||||||
|
disable_monitoring,
|
||||||
|
minimum_availability,
|
||||||
|
quality_profile_id,
|
||||||
|
root_folder_path,
|
||||||
|
tag,
|
||||||
|
clear_tags,
|
||||||
|
} => {
|
||||||
|
let monitored_value = mutex_flags_or_option(enable_monitoring, disable_monitoring);
|
||||||
|
let edit_movie_params = EditMovieParams {
|
||||||
|
movie_id,
|
||||||
|
monitored: monitored_value,
|
||||||
|
minimum_availability,
|
||||||
|
quality_profile_id,
|
||||||
|
root_folder_path,
|
||||||
|
tags: tag,
|
||||||
|
clear_tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
execute_network_event!(
|
||||||
|
self,
|
||||||
|
RadarrEvent::EditMovie(Some(edit_movie_params)),
|
||||||
|
"Movie updated"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{command, Subcommand};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "get_command_handler_tests.rs"]
|
||||||
|
mod get_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrGetCommand {
|
||||||
|
#[command(about = "Get the shared settings for all indexers")]
|
||||||
|
AllIndexerSettings,
|
||||||
|
#[command(about = "Get detailed information for the movie with the given ID")]
|
||||||
|
MovieDetails {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Radarr ID of the movie whose details you wish to fetch",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Get history for the given movie ID")]
|
||||||
|
MovieHistory {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Radarr ID of the movie whose history you wish to fetch",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Get the system status")]
|
||||||
|
SystemStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrGetCommand> for Command {
|
||||||
|
fn from(value: RadarrGetCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::Get(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrGetCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrGetCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrGetCommand> for RadarrGetCommandHandler<'a, 'b> {
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrGetCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrGetCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrGetCommand::AllIndexerSettings => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetAllIndexerSettings);
|
||||||
|
}
|
||||||
|
RadarrGetCommand::MovieDetails { movie_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetMovieDetails(Some(movie_id)));
|
||||||
|
}
|
||||||
|
RadarrGetCommand::MovieHistory { movie_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetMovieHistory(Some(movie_id)));
|
||||||
|
}
|
||||||
|
RadarrGetCommand::SystemStatus => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
use crate::cli::radarr::get_command_handler::RadarrGetCommand;
|
||||||
|
use crate::cli::radarr::RadarrCommand;
|
||||||
|
use crate::cli::Command;
|
||||||
|
use crate::Cli;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_get_command_from() {
|
||||||
|
let command = RadarrGetCommand::AllIndexerSettings;
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(RadarrCommand::Get(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_all_indexer_settings_has_no_arg_requirements() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "all-indexer-settings"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movie_details_requires_movie_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "movie-details"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movie_details_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"get",
|
||||||
|
"movie-details",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movie_history_requires_movie_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "movie-history"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movie_history_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"get",
|
||||||
|
"movie-history",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_status_has_no_arg_requirements() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "get", "system-status"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{
|
||||||
|
radarr::get_command_handler::{RadarrGetCommand, RadarrGetCommandHandler},
|
||||||
|
CliCommandHandler,
|
||||||
|
},
|
||||||
|
models::{radarr_models::RadarrSerdeable, Serdeable},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_all_indexer_settings_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetAllIndexerSettings.into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let get_all_indexer_settings_command = RadarrGetCommand::AllIndexerSettings;
|
||||||
|
|
||||||
|
let result = RadarrGetCommandHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
get_all_indexer_settings_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_movie_details_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetMovieDetails(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let get_movie_details_command = RadarrGetCommand::MovieDetails { movie_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrGetCommandHandler::with(&app_arc, get_movie_details_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_movie_history_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetMovieHistory(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let get_movie_history_command = RadarrGetCommand::MovieHistory { movie_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrGetCommandHandler::with(&app_arc, get_movie_history_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_system_status_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::GetStatus.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let get_system_status_command = RadarrGetCommand::SystemStatus;
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrGetCommandHandler::with(&app_arc, get_system_status_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{command, Subcommand};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "list_command_handler_tests.rs"]
|
||||||
|
mod list_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrListCommand {
|
||||||
|
#[command(about = "List all items in the Radarr blocklist")]
|
||||||
|
Blocklist,
|
||||||
|
#[command(about = "List all Radarr collections")]
|
||||||
|
Collections,
|
||||||
|
#[command(about = "List all active downloads in Radarr")]
|
||||||
|
Downloads,
|
||||||
|
#[command(about = "List all Radarr indexers")]
|
||||||
|
Indexers,
|
||||||
|
#[command(about = "Fetch Radarr logs")]
|
||||||
|
Logs {
|
||||||
|
#[arg(long, help = "How many log events to fetch", default_value_t = 500)]
|
||||||
|
events: u64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Output the logs in the same format as they appear in the log files"
|
||||||
|
)]
|
||||||
|
output_in_log_format: bool,
|
||||||
|
},
|
||||||
|
#[command(about = "List all movies in your Radarr library")]
|
||||||
|
Movies,
|
||||||
|
#[command(about = "Get the credits for the movie with the given ID")]
|
||||||
|
MovieCredits {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Radarr ID of the movie whose credits you wish to fetch",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "List all Radarr quality profiles")]
|
||||||
|
QualityProfiles,
|
||||||
|
#[command(about = "List all queued events")]
|
||||||
|
QueuedEvents,
|
||||||
|
#[command(about = "List all root folders in Radarr")]
|
||||||
|
RootFolders,
|
||||||
|
#[command(about = "List all Radarr tags")]
|
||||||
|
Tags,
|
||||||
|
#[command(about = "List tasks")]
|
||||||
|
Tasks,
|
||||||
|
#[command(about = "List all Radarr updates")]
|
||||||
|
Updates,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrListCommand> for Command {
|
||||||
|
fn from(value: RadarrListCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::List(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrListCommandHandler<'a, 'b> {
|
||||||
|
app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrListCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandHandler<'a, 'b> {
|
||||||
|
fn with(
|
||||||
|
app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrListCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrListCommandHandler {
|
||||||
|
app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrListCommand::Blocklist => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetBlocklist);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Collections => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetCollections);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Downloads => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetDownloads);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Indexers => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetIndexers);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Logs {
|
||||||
|
events,
|
||||||
|
output_in_log_format,
|
||||||
|
} => {
|
||||||
|
let logs = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(RadarrEvent::GetLogs(Some(events)).into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if output_in_log_format {
|
||||||
|
let log_lines = self.app.lock().await.data.radarr_data.logs.items.clone();
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(&log_lines)?;
|
||||||
|
println!("{}", json);
|
||||||
|
} else {
|
||||||
|
let json = serde_json::to_string_pretty(&logs)?;
|
||||||
|
println!("{}", json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RadarrListCommand::Movies => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetMovies);
|
||||||
|
}
|
||||||
|
RadarrListCommand::MovieCredits { movie_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetMovieCredits(Some(movie_id)));
|
||||||
|
}
|
||||||
|
RadarrListCommand::QualityProfiles => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetQualityProfiles);
|
||||||
|
}
|
||||||
|
RadarrListCommand::QueuedEvents => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetQueuedEvents);
|
||||||
|
}
|
||||||
|
RadarrListCommand::RootFolders => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetRootFolders);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Tags => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetTags);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Tasks => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetTasks);
|
||||||
|
}
|
||||||
|
RadarrListCommand::Updates => {
|
||||||
|
execute_network_event!(self, RadarrEvent::GetUpdates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
use crate::cli::radarr::list_command_handler::RadarrListCommand;
|
||||||
|
use crate::cli::radarr::RadarrCommand;
|
||||||
|
use crate::cli::Command;
|
||||||
|
use crate::Cli;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_list_command_from() {
|
||||||
|
let command = RadarrListCommand::Movies;
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(RadarrCommand::List(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use clap::Parser;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_list_commands_have_no_arg_requirements(
|
||||||
|
#[values(
|
||||||
|
"blocklist",
|
||||||
|
"collections",
|
||||||
|
"downloads",
|
||||||
|
"indexers",
|
||||||
|
"movies",
|
||||||
|
"quality-profiles",
|
||||||
|
"queued-events",
|
||||||
|
"root-folders",
|
||||||
|
"tags",
|
||||||
|
"tasks",
|
||||||
|
"updates"
|
||||||
|
)]
|
||||||
|
subcommand: &str,
|
||||||
|
) {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "list", subcommand]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_movie_credits_requires_movie_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "list", "movie-credits"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_logs_events_flag_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "list", "logs", "--events"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_movie_credits_success() {
|
||||||
|
let expected_args = RadarrListCommand::MovieCredits { movie_id: 1 };
|
||||||
|
let result = Cli::try_parse_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"list",
|
||||||
|
"movie-credits",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::List(refresh_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(refresh_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_logs_default_values() {
|
||||||
|
let expected_args = RadarrListCommand::Logs {
|
||||||
|
events: 500,
|
||||||
|
output_in_log_format: false,
|
||||||
|
};
|
||||||
|
let result = Cli::try_parse_from(["managarr", "radarr", "list", "logs"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::List(refresh_command))) = result.unwrap().command {
|
||||||
|
assert_eq!(refresh_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::cli::CliCommandHandler;
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::radarr::list_command_handler::{RadarrListCommand, RadarrListCommandHandler},
|
||||||
|
models::{radarr_models::RadarrSerdeable, Serdeable},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(RadarrListCommand::Blocklist, RadarrEvent::GetBlocklist)]
|
||||||
|
#[case(RadarrListCommand::Collections, RadarrEvent::GetCollections)]
|
||||||
|
#[case(RadarrListCommand::Downloads, RadarrEvent::GetDownloads)]
|
||||||
|
#[case(RadarrListCommand::Indexers, RadarrEvent::GetIndexers)]
|
||||||
|
#[case(RadarrListCommand::Movies, RadarrEvent::GetMovies)]
|
||||||
|
#[case(RadarrListCommand::QualityProfiles, RadarrEvent::GetQualityProfiles)]
|
||||||
|
#[case(RadarrListCommand::QueuedEvents, RadarrEvent::GetQueuedEvents)]
|
||||||
|
#[case(RadarrListCommand::RootFolders, RadarrEvent::GetRootFolders)]
|
||||||
|
#[case(RadarrListCommand::Tags, RadarrEvent::GetTags)]
|
||||||
|
#[case(RadarrListCommand::Tasks, RadarrEvent::GetTasks)]
|
||||||
|
#[case(RadarrListCommand::Updates, RadarrEvent::GetUpdates)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_blocklist_command(
|
||||||
|
#[case] list_command: RadarrListCommand,
|
||||||
|
#[case] expected_radarr_event: RadarrEvent,
|
||||||
|
) {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(expected_radarr_event.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
|
||||||
|
let result = RadarrListCommandHandler::with(&app_arc, list_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_movie_credits_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let list_movie_credits_command = RadarrListCommand::MovieCredits { movie_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrListCommandHandler::with(&app_arc, list_movie_credits_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_logs_command() {
|
||||||
|
let expected_events = 1000;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetLogs(Some(expected_events)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let list_logs_command = RadarrListCommand::Logs {
|
||||||
|
events: 1000,
|
||||||
|
output_in_log_format: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrListCommandHandler::with(&app_arc, list_logs_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use add_command_handler::{RadarrAddCommand, RadarrAddCommandHandler};
|
||||||
|
use clap::Subcommand;
|
||||||
|
use delete_command_handler::{RadarrDeleteCommand, RadarrDeleteCommandHandler};
|
||||||
|
use edit_command_handler::{RadarrEditCommand, RadarrEditCommandHandler};
|
||||||
|
use get_command_handler::{RadarrGetCommand, RadarrGetCommandHandler};
|
||||||
|
use list_command_handler::{RadarrListCommand, RadarrListCommandHandler};
|
||||||
|
use refresh_command_handler::{RadarrRefreshCommand, RadarrRefreshCommandHandler};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
|
use crate::cli::CliCommandHandler;
|
||||||
|
use crate::execute_network_event;
|
||||||
|
use crate::models::radarr_models::{ReleaseDownloadBody, TaskName};
|
||||||
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
|
use crate::network::NetworkTrait;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use super::Command;
|
||||||
|
|
||||||
|
mod add_command_handler;
|
||||||
|
mod delete_command_handler;
|
||||||
|
mod edit_command_handler;
|
||||||
|
mod get_command_handler;
|
||||||
|
mod list_command_handler;
|
||||||
|
mod refresh_command_handler;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "radarr_command_tests.rs"]
|
||||||
|
mod radarr_command_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrCommand {
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to add or create new resources within your Radarr instance"
|
||||||
|
)]
|
||||||
|
Add(RadarrAddCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to delete resources from your Radarr instance"
|
||||||
|
)]
|
||||||
|
Delete(RadarrDeleteCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to edit resources in your Radarr instance"
|
||||||
|
)]
|
||||||
|
Edit(RadarrEditCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to fetch details of the resources in your Radarr instance"
|
||||||
|
)]
|
||||||
|
Get(RadarrGetCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to list attributes from your Radarr instance"
|
||||||
|
)]
|
||||||
|
List(RadarrListCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to refresh the data in your Radarr instance"
|
||||||
|
)]
|
||||||
|
Refresh(RadarrRefreshCommand),
|
||||||
|
#[command(about = "Clear the blocklist")]
|
||||||
|
ClearBlocklist,
|
||||||
|
#[command(about = "Manually download the given release for the specified movie ID")]
|
||||||
|
DownloadRelease {
|
||||||
|
#[arg(long, help = "The GUID of the release to download", required = true)]
|
||||||
|
guid: String,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The indexer ID to download the release from",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
indexer_id: i64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The movie ID that the release is associated with",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Trigger a manual search of releases for the movie with the given ID")]
|
||||||
|
ManualSearch {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The Radarr ID of the movie whose releases you wish to fetch and list",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Search for a new film to add to Radarr")]
|
||||||
|
SearchNewMovie {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The title of the film you want to search for",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
query: String,
|
||||||
|
},
|
||||||
|
#[command(about = "Start the specified Radarr task")]
|
||||||
|
StartTask {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The name of the task to trigger",
|
||||||
|
value_enum,
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
task_name: TaskName,
|
||||||
|
},
|
||||||
|
#[command(
|
||||||
|
about = "Test the indexer with the given ID. Note that a successful test returns an empty JSON body; i.e. '{}'"
|
||||||
|
)]
|
||||||
|
TestIndexer {
|
||||||
|
#[arg(long, help = "The ID of the indexer to test", required = true)]
|
||||||
|
indexer_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Test all indexers")]
|
||||||
|
TestAllIndexers,
|
||||||
|
#[command(about = "Trigger an automatic search for the movie with the specified ID")]
|
||||||
|
TriggerAutomaticSearch {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the movie you want to trigger an automatic search for",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrCommand> for Command {
|
||||||
|
fn from(radarr_command: RadarrCommand) -> Command {
|
||||||
|
Command::Radarr(radarr_command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrCliHandler<'a, 'b> {
|
||||||
|
app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, 'b> {
|
||||||
|
fn with(
|
||||||
|
app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrCliHandler {
|
||||||
|
app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrCommand::Add(add_command) => {
|
||||||
|
RadarrAddCommandHandler::with(self.app, add_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::Delete(delete_command) => {
|
||||||
|
RadarrDeleteCommandHandler::with(self.app, delete_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::Edit(edit_command) => {
|
||||||
|
RadarrEditCommandHandler::with(self.app, edit_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::Get(get_command) => {
|
||||||
|
RadarrGetCommandHandler::with(self.app, get_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::List(list_command) => {
|
||||||
|
RadarrListCommandHandler::with(self.app, list_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::Refresh(update_command) => {
|
||||||
|
RadarrRefreshCommandHandler::with(self.app, update_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
RadarrCommand::ClearBlocklist => {
|
||||||
|
self
|
||||||
|
.network
|
||||||
|
.handle_network_event(RadarrEvent::GetBlocklist.into())
|
||||||
|
.await?;
|
||||||
|
execute_network_event!(self, RadarrEvent::ClearBlocklist);
|
||||||
|
}
|
||||||
|
RadarrCommand::DownloadRelease {
|
||||||
|
guid,
|
||||||
|
indexer_id,
|
||||||
|
movie_id,
|
||||||
|
} => {
|
||||||
|
let params = ReleaseDownloadBody {
|
||||||
|
guid,
|
||||||
|
indexer_id,
|
||||||
|
movie_id,
|
||||||
|
};
|
||||||
|
execute_network_event!(self, RadarrEvent::DownloadRelease(Some(params)));
|
||||||
|
}
|
||||||
|
RadarrCommand::ManualSearch { movie_id } => {
|
||||||
|
println!("Searching for releases. This may take a minute...");
|
||||||
|
execute_network_event!(self, RadarrEvent::GetReleases(Some(movie_id)));
|
||||||
|
}
|
||||||
|
RadarrCommand::SearchNewMovie { query } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::SearchNewMovie(Some(query)));
|
||||||
|
}
|
||||||
|
RadarrCommand::StartTask { task_name } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::StartTask(Some(task_name)));
|
||||||
|
}
|
||||||
|
RadarrCommand::TestIndexer { indexer_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::TestIndexer(Some(indexer_id)));
|
||||||
|
}
|
||||||
|
RadarrCommand::TestAllIndexers => {
|
||||||
|
execute_network_event!(self, RadarrEvent::TestAllIndexers);
|
||||||
|
}
|
||||||
|
RadarrCommand::TriggerAutomaticSearch { movie_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::TriggerAutomaticSearch(Some(movie_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,702 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
use crate::cli::radarr::RadarrCommand;
|
||||||
|
use crate::cli::Command;
|
||||||
|
use crate::Cli;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_command_from() {
|
||||||
|
let command = RadarrCommand::TestAllIndexers;
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_commands_that_have_no_arg_requirements(
|
||||||
|
#[values("clear-blocklist", "test-all-indexers")] subcommand: &str,
|
||||||
|
) {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", subcommand]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_download_release_requires_movie_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"download-release",
|
||||||
|
"--indexer-id",
|
||||||
|
"1",
|
||||||
|
"--guid",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_download_release_requires_guid() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"download-release",
|
||||||
|
"--indexer-id",
|
||||||
|
"1",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_download_release_requires_indexer_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"download-release",
|
||||||
|
"--guid",
|
||||||
|
"1",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_download_release_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"download-release",
|
||||||
|
"--guid",
|
||||||
|
"1",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
"--indexer-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_manual_search_requires_movie_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "manual-search"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_manual_search_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"manual-search",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_search_new_movie_requires_query() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "search-new-movie"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_search_new_movie_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"search-new-movie",
|
||||||
|
"--query",
|
||||||
|
"halo",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_start_task_requires_task_name() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "start-task"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_start_task_task_name_validation() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"start-task",
|
||||||
|
"--task-name",
|
||||||
|
"test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_task_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"start-task",
|
||||||
|
"--task-name",
|
||||||
|
"application-check-update",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_test_indexer_requires_indexer_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "test-indexer"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_test_indexer_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"test-indexer",
|
||||||
|
"--indexer-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_trigger_automatic_search_requires_movie_id() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "trigger-automatic-search"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trigger_automatic_search_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"radarr",
|
||||||
|
"trigger-automatic-search",
|
||||||
|
"--movie-id",
|
||||||
|
"1",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{
|
||||||
|
radarr::{
|
||||||
|
add_command_handler::RadarrAddCommand, delete_command_handler::RadarrDeleteCommand,
|
||||||
|
edit_command_handler::RadarrEditCommand, get_command_handler::RadarrGetCommand,
|
||||||
|
list_command_handler::RadarrListCommand, refresh_command_handler::RadarrRefreshCommand,
|
||||||
|
RadarrCliHandler, RadarrCommand,
|
||||||
|
},
|
||||||
|
CliCommandHandler,
|
||||||
|
},
|
||||||
|
models::{
|
||||||
|
radarr_models::{
|
||||||
|
BlocklistItem, BlocklistResponse, IndexerSettings, RadarrSerdeable, ReleaseDownloadBody,
|
||||||
|
TaskName,
|
||||||
|
},
|
||||||
|
Serdeable,
|
||||||
|
},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_clear_blocklist_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::GetBlocklist.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::BlocklistResponse(
|
||||||
|
BlocklistResponse {
|
||||||
|
records: vec![BlocklistItem::default()],
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::ClearBlocklist.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let claer_blocklist_command = RadarrCommand::ClearBlocklist;
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, claer_blocklist_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_download_release_command() {
|
||||||
|
let expected_release_download_body = ReleaseDownloadBody {
|
||||||
|
guid: "guid".to_owned(),
|
||||||
|
indexer_id: 1,
|
||||||
|
movie_id: 1,
|
||||||
|
};
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DownloadRelease(Some(expected_release_download_body)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let download_release_command = RadarrCommand::DownloadRelease {
|
||||||
|
guid: "guid".to_owned(),
|
||||||
|
indexer_id: 1,
|
||||||
|
movie_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, download_release_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_manual_search_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetReleases(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let manual_search_command = RadarrCommand::ManualSearch { movie_id: 1 };
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, manual_search_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search_new_movie_command() {
|
||||||
|
let expected_search_query = "halo".to_owned();
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::SearchNewMovie(Some(expected_search_query)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let search_new_movie_command = RadarrCommand::SearchNewMovie {
|
||||||
|
query: "halo".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, search_new_movie_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_start_task_command() {
|
||||||
|
let expected_task_name = TaskName::ApplicationCheckUpdate;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::StartTask(Some(expected_task_name)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let start_task_command = RadarrCommand::StartTask {
|
||||||
|
task_name: TaskName::ApplicationCheckUpdate,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, start_task_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_test_indexer_command() {
|
||||||
|
let expected_indexer_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::TestIndexer(Some(expected_indexer_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let test_indexer_command = RadarrCommand::TestIndexer { indexer_id: 1 };
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, test_indexer_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_test_all_indexers_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(RadarrEvent::TestAllIndexers.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let test_all_indexers_command = RadarrCommand::TestAllIndexers;
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, test_all_indexers_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_trigger_automatic_search_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::TriggerAutomaticSearch(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let trigger_automatic_search_command = RadarrCommand::TriggerAutomaticSearch { movie_id: 1 };
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
trigger_automatic_search_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_add_commands_to_the_add_command_handler() {
|
||||||
|
let expected_tag_name = "test".to_owned();
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::AddTag(expected_tag_name.clone()).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let add_tag_command = RadarrCommand::Add(RadarrAddCommand::Tag {
|
||||||
|
name: expected_tag_name,
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, add_tag_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_delete_commands_to_the_delete_command_handler() {
|
||||||
|
let expected_blocklist_item_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let delete_blocklist_item_command =
|
||||||
|
RadarrCommand::Delete(RadarrDeleteCommand::BlocklistItem {
|
||||||
|
blocklist_item_id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrCliHandler::with(&app_arc, delete_blocklist_item_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_edit_commands_to_the_edit_command_handler() {
|
||||||
|
let expected_edit_all_indexer_settings = IndexerSettings {
|
||||||
|
allow_hardcoded_subs: true,
|
||||||
|
availability_delay: 1,
|
||||||
|
id: 1,
|
||||||
|
maximum_size: 1,
|
||||||
|
minimum_age: 1,
|
||||||
|
prefer_indexer_flags: true,
|
||||||
|
retention: 1,
|
||||||
|
rss_sync_interval: 1,
|
||||||
|
whitelisted_hardcoded_subs: "test".into(),
|
||||||
|
};
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetAllIndexerSettings.into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::IndexerSettings(
|
||||||
|
IndexerSettings {
|
||||||
|
allow_hardcoded_subs: false,
|
||||||
|
availability_delay: 2,
|
||||||
|
id: 1,
|
||||||
|
maximum_size: 2,
|
||||||
|
minimum_age: 2,
|
||||||
|
prefer_indexer_flags: false,
|
||||||
|
retention: 2,
|
||||||
|
rss_sync_interval: 2,
|
||||||
|
whitelisted_hardcoded_subs: "testing".into(),
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let edit_all_indexer_settings_command =
|
||||||
|
RadarrCommand::Edit(RadarrEditCommand::AllIndexerSettings {
|
||||||
|
allow_hardcoded_subs: true,
|
||||||
|
disable_allow_hardcoded_subs: false,
|
||||||
|
availability_delay: Some(1),
|
||||||
|
maximum_size: Some(1),
|
||||||
|
minimum_age: Some(1),
|
||||||
|
prefer_indexer_flags: true,
|
||||||
|
disable_prefer_indexer_flags: false,
|
||||||
|
retention: Some(1),
|
||||||
|
rss_sync_interval: Some(1),
|
||||||
|
whitelisted_subtitle_tags: Some("test".to_owned()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
edit_all_indexer_settings_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_get_commands_to_the_get_command_handler() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetAllIndexerSettings.into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let get_all_indexer_settings_command =
|
||||||
|
RadarrCommand::Get(RadarrGetCommand::AllIndexerSettings);
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(
|
||||||
|
&app_arc,
|
||||||
|
get_all_indexer_settings_command,
|
||||||
|
&mut mock_network,
|
||||||
|
)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_list_commands_to_the_list_command_handler() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let list_movie_credits_command =
|
||||||
|
RadarrCommand::List(RadarrListCommand::MovieCredits { movie_id: 1 });
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, list_movie_credits_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_radarr_cli_handler_delegates_refresh_commands_to_the_refresh_command_handler() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let refresh_movie_command =
|
||||||
|
RadarrCommand::Refresh(RadarrRefreshCommand::Movie { movie_id: 1 });
|
||||||
|
|
||||||
|
let result = RadarrCliHandler::with(&app_arc, refresh_movie_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Subcommand;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
execute_network_event,
|
||||||
|
network::{radarr_network::RadarrEvent, NetworkTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::RadarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "refresh_command_handler_tests.rs"]
|
||||||
|
mod refresh_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum RadarrRefreshCommand {
|
||||||
|
#[command(about = "Refresh all movie data for all movies in your library")]
|
||||||
|
AllMovies,
|
||||||
|
#[command(about = "Refresh movie data and scan disk for the movie with the given ID")]
|
||||||
|
Movie {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The ID of the movie to refresh information on and to scan the disk for",
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
movie_id: i64,
|
||||||
|
},
|
||||||
|
#[command(about = "Refresh all collection data for all collections in your library")]
|
||||||
|
Collections,
|
||||||
|
#[command(about = "Refresh all downloads in Radarr")]
|
||||||
|
Downloads,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrRefreshCommand> for Command {
|
||||||
|
fn from(value: RadarrRefreshCommand) -> Self {
|
||||||
|
Command::Radarr(RadarrCommand::Refresh(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct RadarrRefreshCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrRefreshCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrRefreshCommand>
|
||||||
|
for RadarrRefreshCommandHandler<'a, 'b>
|
||||||
|
{
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: RadarrRefreshCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
RadarrRefreshCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> Result<()> {
|
||||||
|
match self.command {
|
||||||
|
RadarrRefreshCommand::AllMovies => {
|
||||||
|
execute_network_event!(self, RadarrEvent::UpdateAllMovies);
|
||||||
|
}
|
||||||
|
RadarrRefreshCommand::Collections => {
|
||||||
|
execute_network_event!(self, RadarrEvent::UpdateCollections);
|
||||||
|
}
|
||||||
|
RadarrRefreshCommand::Downloads => {
|
||||||
|
execute_network_event!(self, RadarrEvent::UpdateDownloads);
|
||||||
|
}
|
||||||
|
RadarrRefreshCommand::Movie { movie_id } => {
|
||||||
|
execute_network_event!(self, RadarrEvent::UpdateAndScan(Some(movie_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
use crate::cli::radarr::refresh_command_handler::RadarrRefreshCommand;
|
||||||
|
use crate::cli::radarr::RadarrCommand;
|
||||||
|
use crate::cli::Command;
|
||||||
|
use crate::Cli;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_refresh_command_from() {
|
||||||
|
let command = RadarrRefreshCommand::AllMovies;
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Radarr(RadarrCommand::Refresh(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
use clap::Parser;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_refresh_commands_have_no_arg_requirements(
|
||||||
|
#[values("all-movies", "collections", "downloads")] subcommand: &str,
|
||||||
|
) {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "radarr", "refresh", subcommand]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_refresh_movie_requires_movie_id() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "radarr", "refresh", "movie"]);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_refresh_movie_success() {
|
||||||
|
let expected_args = RadarrRefreshCommand::Movie { movie_id: 1 };
|
||||||
|
let result =
|
||||||
|
Cli::try_parse_from(["managarr", "radarr", "refresh", "movie", "--movie-id", "1"]);
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Some(Command::Radarr(RadarrCommand::Refresh(refresh_command))) =
|
||||||
|
result.unwrap().command
|
||||||
|
{
|
||||||
|
assert_eq!(refresh_command, expected_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use rstest::rstest;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::cli::CliCommandHandler;
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::radarr::refresh_command_handler::{RadarrRefreshCommand, RadarrRefreshCommandHandler},
|
||||||
|
models::{radarr_models::RadarrSerdeable, Serdeable},
|
||||||
|
network::{radarr_network::RadarrEvent, MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(RadarrRefreshCommand::AllMovies, RadarrEvent::UpdateAllMovies)]
|
||||||
|
#[case(RadarrRefreshCommand::Collections, RadarrEvent::UpdateCollections)]
|
||||||
|
#[case(RadarrRefreshCommand::Downloads, RadarrEvent::UpdateDownloads)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_blocklist_command(
|
||||||
|
#[case] refresh_command: RadarrRefreshCommand,
|
||||||
|
#[case] expected_radarr_event: RadarrEvent,
|
||||||
|
) {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(expected_radarr_event.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
|
||||||
|
let result = RadarrRefreshCommandHandler::with(&app_arc, refresh_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_refresh_movie_command() {
|
||||||
|
let expected_movie_id = 1;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
|
let refresh_movie_command = RadarrRefreshCommand::Movie { movie_id: 1 };
|
||||||
|
|
||||||
|
let result =
|
||||||
|
RadarrRefreshCommandHandler::with(&app_arc, refresh_movie_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -410,7 +410,7 @@ mod tests {
|
|||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::Blocklist,
|
ActiveRadarrBlock::Blocklist,
|
||||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
|
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
|
||||||
RadarrEvent::DeleteBlocklistItem
|
RadarrEvent::DeleteBlocklistItem(None)
|
||||||
)]
|
)]
|
||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::Blocklist,
|
ActiveRadarrBlock::Blocklist,
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
|
|||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
|
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteBlocklistItem);
|
self.app.data.radarr_data.prompt_confirm_action =
|
||||||
|
Some(RadarrEvent::DeleteBlocklistItem(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use core::sync::atomic::Ordering::SeqCst;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
@@ -231,7 +232,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -239,7 +240,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -252,7 +253,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -260,7 +261,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -284,7 +285,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -292,7 +293,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -305,7 +306,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -313,7 +314,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -458,7 +459,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -466,7 +467,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -479,7 +480,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -487,7 +488,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -506,7 +507,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -514,7 +515,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -527,7 +528,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.collections
|
.collections
|
||||||
@@ -535,7 +536,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
|
|||||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||||
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
|
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection);
|
self.app.data.radarr_data.prompt_confirm_action =
|
||||||
|
Some(RadarrEvent::EditCollection(None));
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -200,6 +200,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
@@ -337,7 +339,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_collection_modal
|
.edit_collection_modal
|
||||||
@@ -345,7 +347,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -358,7 +360,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_collection_modal
|
.edit_collection_modal
|
||||||
@@ -366,13 +368,15 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -420,7 +424,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_collection_modal
|
.edit_collection_modal
|
||||||
@@ -428,7 +432,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -441,7 +445,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_collection_modal
|
.edit_collection_modal
|
||||||
@@ -449,7 +453,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -561,7 +565,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::EditCollection)
|
Some(RadarrEvent::EditCollection(None))
|
||||||
);
|
);
|
||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ mod tests {
|
|||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::Downloads,
|
ActiveRadarrBlock::Downloads,
|
||||||
ActiveRadarrBlock::DeleteDownloadPrompt,
|
ActiveRadarrBlock::DeleteDownloadPrompt,
|
||||||
RadarrEvent::DeleteDownload
|
RadarrEvent::DeleteDownload(None)
|
||||||
)]
|
)]
|
||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::Downloads,
|
ActiveRadarrBlock::Downloads,
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
|
|||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::DeleteDownloadPrompt => {
|
ActiveRadarrBlock::DeleteDownloadPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
|
|||||||
ActiveRadarrBlock::EditIndexerConfirmPrompt => {
|
ActiveRadarrBlock::EditIndexerConfirmPrompt => {
|
||||||
let radarr_data = &mut self.app.data.radarr_data;
|
let radarr_data = &mut self.app.data.radarr_data;
|
||||||
if radarr_data.prompt_confirm {
|
if radarr_data.prompt_confirm {
|
||||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer);
|
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
} else {
|
} else {
|
||||||
radarr_data.edit_indexer_modal = None;
|
radarr_data.edit_indexer_modal = None;
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::modals::EditIndexerModal;
|
use crate::models::servarr_data::radarr::modals::EditIndexerModal;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@@ -89,7 +91,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -97,7 +99,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.name
|
.name
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -118,7 +120,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.name
|
.name
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -140,7 +142,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -148,7 +150,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.url
|
.url
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -161,7 +163,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -169,7 +171,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.url
|
.url
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -191,7 +193,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -199,7 +201,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.api_key
|
.api_key
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -212,7 +214,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -220,7 +222,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.api_key
|
.api_key
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -242,7 +244,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -250,7 +252,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.seed_ratio
|
.seed_ratio
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -263,7 +265,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -271,7 +273,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.seed_ratio
|
.seed_ratio
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -293,7 +295,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -301,7 +303,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -314,7 +316,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -322,13 +324,15 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::modals::EditIndexerModal;
|
use crate::models::servarr_data::radarr::modals::EditIndexerModal;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{
|
use crate::models::servarr_data::radarr::radarr_data::{
|
||||||
@@ -511,7 +515,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -519,7 +523,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.name
|
.name
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -532,7 +536,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -540,7 +544,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.name
|
.name
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -562,7 +566,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -570,7 +574,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.url
|
.url
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -583,7 +587,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -591,7 +595,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.url
|
.url
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -613,7 +617,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -621,7 +625,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.api_key
|
.api_key
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -634,7 +638,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -642,7 +646,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.api_key
|
.api_key
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -664,7 +668,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -672,7 +676,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.seed_ratio
|
.seed_ratio
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -685,7 +689,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -693,7 +697,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.seed_ratio
|
.seed_ratio
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -715,7 +719,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -723,7 +727,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -736,7 +740,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_indexer_modal
|
.edit_indexer_modal
|
||||||
@@ -744,7 +748,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -821,7 +825,7 @@ mod tests {
|
|||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::EditIndexer)
|
Some(RadarrEvent::EditIndexer(None))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
fn handle_scroll_up(&mut self) {
|
fn handle_scroll_up(&mut self) {
|
||||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
self.app.data.radarr_data.selected_block.previous();
|
self.app.data.radarr_data.selected_block.previous();
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
||||||
@@ -74,7 +74,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
fn handle_scroll_down(&mut self) {
|
fn handle_scroll_down(&mut self) {
|
||||||
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap();
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => self.app.data.radarr_data.selected_block.next(),
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
|
self.app.data.radarr_data.selected_block.next()
|
||||||
|
}
|
||||||
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
ActiveRadarrBlock::IndexerSettingsMinimumAgeInput => {
|
||||||
if indexer_settings.minimum_age > 0 {
|
if indexer_settings.minimum_age > 0 {
|
||||||
indexer_settings.minimum_age -= 1;
|
indexer_settings.minimum_age -= 1;
|
||||||
@@ -134,7 +136,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
|
|
||||||
fn handle_left_right_action(&mut self) {
|
fn handle_left_right_action(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
if self.app.data.radarr_data.selected_block.get_active_block()
|
if self.app.data.radarr_data.selected_block.get_active_block()
|
||||||
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
|
== &ActiveRadarrBlock::IndexerSettingsConfirmPrompt
|
||||||
{
|
{
|
||||||
@@ -165,12 +167,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
|
|
||||||
fn handle_submit(&mut self) {
|
fn handle_submit(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
|
||||||
let radarr_data = &mut self.app.data.radarr_data;
|
let radarr_data = &mut self.app.data.radarr_data;
|
||||||
if radarr_data.prompt_confirm {
|
if radarr_data.prompt_confirm {
|
||||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditAllIndexerSettings);
|
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditAllIndexerSettings(None));
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
} else {
|
} else {
|
||||||
radarr_data.indexer_settings = None;
|
radarr_data.indexer_settings = None;
|
||||||
@@ -225,7 +227,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
|
|||||||
|
|
||||||
fn handle_esc(&mut self) {
|
fn handle_esc(&mut self) {
|
||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt => {
|
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
self.app.data.radarr_data.prompt_confirm = false;
|
self.app.data.radarr_data.prompt_confirm = false;
|
||||||
self.app.data.radarr_data.indexer_settings = None;
|
self.app.data.radarr_data.indexer_settings = None;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -136,7 +136,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -201,6 +201,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::models::radarr_models::IndexerSettings;
|
use crate::models::radarr_models::IndexerSettings;
|
||||||
@@ -224,7 +226,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.indexer_settings
|
.indexer_settings
|
||||||
@@ -232,7 +234,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.whitelisted_hardcoded_subs
|
.whitelisted_hardcoded_subs
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -245,7 +247,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.indexer_settings
|
.indexer_settings
|
||||||
@@ -253,13 +255,15 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.whitelisted_hardcoded_subs
|
.whitelisted_hardcoded_subs
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::models::radarr_models::IndexerSettings;
|
use crate::models::radarr_models::IndexerSettings;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
|
use crate::models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS;
|
||||||
use crate::models::BlockSelectionState;
|
use crate::models::BlockSelectionState;
|
||||||
@@ -278,7 +282,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -288,7 +292,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -336,7 +340,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -349,7 +353,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&key,
|
&key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -377,7 +381,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.indexer_settings
|
.indexer_settings
|
||||||
@@ -385,7 +389,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.whitelisted_hardcoded_subs
|
.whitelisted_hardcoded_subs
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -398,7 +402,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.indexer_settings
|
.indexer_settings
|
||||||
@@ -406,7 +410,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.whitelisted_hardcoded_subs
|
.whitelisted_hardcoded_subs
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -432,7 +436,7 @@ mod tests {
|
|||||||
fn test_edit_indexer_settings_prompt_prompt_decline_submit() {
|
fn test_edit_indexer_settings_prompt_prompt_decline_submit() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app
|
app
|
||||||
@@ -445,7 +449,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -460,7 +464,7 @@ mod tests {
|
|||||||
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit() {
|
fn test_edit_indexer_settings_prompt_prompt_confirmation_submit() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app
|
app
|
||||||
@@ -474,7 +478,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -482,7 +486,7 @@ mod tests {
|
|||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::EditAllIndexerSettings)
|
Some(RadarrEvent::EditAllIndexerSettings(None))
|
||||||
);
|
);
|
||||||
assert!(app.data.radarr_data.indexer_settings.is_some());
|
assert!(app.data.radarr_data.indexer_settings.is_some());
|
||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
@@ -493,21 +497,21 @@ mod tests {
|
|||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.is_loading = true;
|
app.is_loading = true;
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
app.data.radarr_data.prompt_confirm = true;
|
app.data.radarr_data.prompt_confirm = true;
|
||||||
|
|
||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert!(!app.should_refresh);
|
assert!(!app.should_refresh);
|
||||||
}
|
}
|
||||||
@@ -524,7 +528,7 @@ mod tests {
|
|||||||
) {
|
) {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app.data.radarr_data.selected_block.set_index(index);
|
app.data.radarr_data.selected_block.set_index(index);
|
||||||
@@ -532,7 +536,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -547,7 +551,7 @@ mod tests {
|
|||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.is_loading = true;
|
app.is_loading = true;
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app.data.radarr_data.selected_block.set_index(index);
|
app.data.radarr_data.selected_block.set_index(index);
|
||||||
@@ -555,14 +559,14 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +574,7 @@ mod tests {
|
|||||||
fn test_edit_indexer_settings_prompt_submit_whitelisted_subtitle_tags_input() {
|
fn test_edit_indexer_settings_prompt_submit_whitelisted_subtitle_tags_input() {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app.data.radarr_data.selected_block.set_index(7);
|
app.data.radarr_data.selected_block.set_index(7);
|
||||||
@@ -578,7 +582,7 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -597,19 +601,19 @@ mod tests {
|
|||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app.data.radarr_data.selected_block.set_index(3);
|
app.data.radarr_data.selected_block.set_index(3);
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
|
|
||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
app
|
app
|
||||||
@@ -624,14 +628,14 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!app
|
!app
|
||||||
@@ -651,19 +655,19 @@ mod tests {
|
|||||||
app.data.radarr_data.selected_block =
|
app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
app.data.radarr_data.selected_block.set_index(8);
|
app.data.radarr_data.selected_block.set_index(8);
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
|
|
||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
app
|
app
|
||||||
@@ -678,14 +682,14 @@ mod tests {
|
|||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&SUBMIT_KEY,
|
&SUBMIT_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!app
|
!app
|
||||||
@@ -706,7 +710,7 @@ mod tests {
|
|||||||
whitelisted_hardcoded_subs: "Test tags".into(),
|
whitelisted_hardcoded_subs: "Test tags".into(),
|
||||||
..IndexerSettings::default()
|
..IndexerSettings::default()
|
||||||
});
|
});
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.push_navigation_stack(
|
app.push_navigation_stack(
|
||||||
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into(),
|
ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput.into(),
|
||||||
);
|
);
|
||||||
@@ -731,7 +735,7 @@ mod tests {
|
|||||||
.is_empty());
|
.is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,14 +752,14 @@ mod tests {
|
|||||||
) {
|
) {
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.push_navigation_stack(active_radarr_block.into());
|
app.push_navigation_stack(active_radarr_block.into());
|
||||||
|
|
||||||
IndexerSettingsHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block, &None).handle();
|
IndexerSettingsHandler::with(&SUBMIT_KEY, &mut app, &active_radarr_block, &None).handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,13 +779,13 @@ mod tests {
|
|||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.is_loading = is_ready;
|
app.is_loading = is_ready;
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
|
||||||
app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
app.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
|
||||||
|
|
||||||
IndexerSettingsHandler::with(
|
IndexerSettingsHandler::with(
|
||||||
&ESC_KEY,
|
&ESC_KEY,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
)
|
)
|
||||||
.handle();
|
.handle();
|
||||||
@@ -926,7 +930,7 @@ mod tests {
|
|||||||
let handler = IndexerSettingsHandler::with(
|
let handler = IndexerSettingsHandler::with(
|
||||||
&DEFAULT_KEYBINDINGS.esc.key,
|
&DEFAULT_KEYBINDINGS.esc.key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -941,7 +945,7 @@ mod tests {
|
|||||||
let handler = IndexerSettingsHandler::with(
|
let handler = IndexerSettingsHandler::with(
|
||||||
&DEFAULT_KEYBINDINGS.esc.key,
|
&DEFAULT_KEYBINDINGS.esc.key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -957,7 +961,7 @@ mod tests {
|
|||||||
let handler = IndexerSettingsHandler::with(
|
let handler = IndexerSettingsHandler::with(
|
||||||
&DEFAULT_KEYBINDINGS.esc.key,
|
&DEFAULT_KEYBINDINGS.esc.key,
|
||||||
&mut app,
|
&mut app,
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt,
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
&None,
|
&None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ mod tests {
|
|||||||
assert!(app.data.radarr_data.prompt_confirm);
|
assert!(app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::DeleteIndexer)
|
Some(RadarrEvent::DeleteIndexer(None))
|
||||||
);
|
);
|
||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into());
|
||||||
}
|
}
|
||||||
@@ -577,7 +577,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
&ActiveRadarrBlock::IndexerSettingsPrompt.into()
|
&ActiveRadarrBlock::AllIndexerSettingsPrompt.into()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.selected_block.blocks,
|
app.data.radarr_data.selected_block.blocks,
|
||||||
@@ -724,7 +724,7 @@ mod tests {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
|
fn test_delegates_indexer_settings_blocks_to_indexer_settings_handler(
|
||||||
#[values(
|
#[values(
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt,
|
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
|||||||
ActiveRadarrBlock::DeleteIndexerPrompt => {
|
ActiveRadarrBlock::DeleteIndexerPrompt => {
|
||||||
let radarr_data = &mut self.app.data.radarr_data;
|
let radarr_data = &mut self.app.data.radarr_data;
|
||||||
if radarr_data.prompt_confirm {
|
if radarr_data.prompt_confirm {
|
||||||
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer);
|
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
@@ -189,7 +189,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
|
|||||||
_ if *key == DEFAULT_KEYBINDINGS.settings.key => {
|
_ if *key == DEFAULT_KEYBINDINGS.settings.key => {
|
||||||
self
|
self
|
||||||
.app
|
.app
|
||||||
.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into());
|
.push_navigation_stack(ActiveRadarrBlock::AllIndexerSettingsPrompt.into());
|
||||||
self.app.data.radarr_data.selected_block =
|
self.app.data.radarr_data.selected_block =
|
||||||
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
|
|||||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||||
ActiveRadarrBlock::AddMovieConfirmPrompt => {
|
ActiveRadarrBlock::AddMovieConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -416,6 +416,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::extended_stateful_iterable_vec;
|
use crate::extended_stateful_iterable_vec;
|
||||||
@@ -769,14 +771,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_search
|
.add_movie_search
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -789,14 +791,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_search
|
.add_movie_search
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -818,7 +820,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_modal
|
.add_movie_modal
|
||||||
@@ -826,7 +828,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -839,7 +841,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_modal
|
.add_movie_modal
|
||||||
@@ -847,13 +849,15 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::modals::AddMovieModal;
|
use crate::models::servarr_data::radarr::modals::AddMovieModal;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -886,14 +890,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_search
|
.add_movie_search
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -906,14 +910,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_search
|
.add_movie_search
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -935,7 +939,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_modal
|
.add_movie_modal
|
||||||
@@ -943,7 +947,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -956,7 +960,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_modal
|
.add_movie_modal
|
||||||
@@ -964,7 +968,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1211,7 +1215,7 @@ mod tests {
|
|||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::AddMovie)
|
Some(RadarrEvent::AddMovie(None))
|
||||||
);
|
);
|
||||||
assert!(app.data.radarr_data.add_movie_modal.is_some());
|
assert!(app.data.radarr_data.add_movie_modal.is_some());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
|
|||||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||||
ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
|
ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
} else {
|
} else {
|
||||||
self.app.data.radarr_data.reset_delete_movie_preferences();
|
self.app.data.radarr_data.reset_delete_movie_preferences();
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ mod tests {
|
|||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::DeleteMovie)
|
Some(RadarrEvent::DeleteMovie(None))
|
||||||
);
|
);
|
||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
assert!(app.data.radarr_data.prompt_confirm);
|
assert!(app.data.radarr_data.prompt_confirm);
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
|
|||||||
match self.app.data.radarr_data.selected_block.get_active_block() {
|
match self.app.data.radarr_data.selected_block.get_active_block() {
|
||||||
ActiveRadarrBlock::EditMovieConfirmPrompt => {
|
ActiveRadarrBlock::EditMovieConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None));
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
||||||
@@ -318,7 +320,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -326,7 +328,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -339,7 +341,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -347,7 +349,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -369,7 +371,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -377,7 +379,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -390,7 +392,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -398,13 +400,15 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
use crate::models::servarr_data::radarr::modals::EditMovieModal;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -440,7 +444,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -448,7 +452,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -461,7 +465,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -469,7 +473,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.path
|
.path
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -491,7 +495,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -499,7 +503,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -512,7 +516,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_movie_modal
|
.edit_movie_modal
|
||||||
@@ -520,7 +524,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.tags
|
.tags
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -661,7 +665,7 @@ mod tests {
|
|||||||
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::EditMovie)
|
Some(RadarrEvent::EditMovie(None))
|
||||||
);
|
);
|
||||||
assert!(app.data.radarr_data.edit_movie_modal.is_some());
|
assert!(app.data.radarr_data.edit_movie_modal.is_some());
|
||||||
assert!(app.should_refresh);
|
assert!(app.should_refresh);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use core::sync::atomic::Ordering::SeqCst;
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -213,7 +214,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -221,7 +222,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -242,7 +243,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -266,7 +267,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -274,7 +275,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -287,7 +288,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -295,7 +296,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -488,7 +489,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -496,7 +497,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -509,7 +510,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -517,7 +518,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -536,7 +537,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -544,7 +545,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -557,7 +558,7 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.movies
|
.movies
|
||||||
@@ -565,7 +566,7 @@ mod tests {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -349,14 +349,14 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
|
|||||||
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => {
|
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action =
|
self.app.data.radarr_data.prompt_confirm_action =
|
||||||
Some(RadarrEvent::TriggerAutomaticSearch);
|
Some(RadarrEvent::TriggerAutomaticSearch(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
}
|
}
|
||||||
ActiveRadarrBlock::UpdateAndScanPrompt => {
|
ActiveRadarrBlock::UpdateAndScanPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
@@ -368,7 +368,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
|
|||||||
}
|
}
|
||||||
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
|
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease);
|
self.app.data.radarr_data.prompt_confirm_action =
|
||||||
|
Some(RadarrEvent::DownloadRelease(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -1292,12 +1292,15 @@ mod tests {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
|
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
|
||||||
RadarrEvent::TriggerAutomaticSearch
|
RadarrEvent::TriggerAutomaticSearch(None)
|
||||||
|
)]
|
||||||
|
#[case(
|
||||||
|
ActiveRadarrBlock::UpdateAndScanPrompt,
|
||||||
|
RadarrEvent::UpdateAndScan(None)
|
||||||
)]
|
)]
|
||||||
#[case(ActiveRadarrBlock::UpdateAndScanPrompt, RadarrEvent::UpdateAndScan)]
|
|
||||||
#[case(
|
#[case(
|
||||||
ActiveRadarrBlock::ManualSearchConfirmPrompt,
|
ActiveRadarrBlock::ManualSearchConfirmPrompt,
|
||||||
RadarrEvent::DownloadRelease
|
RadarrEvent::DownloadRelease(None)
|
||||||
)]
|
)]
|
||||||
fn test_movie_info_prompt_confirm_submit(
|
fn test_movie_info_prompt_confirm_submit(
|
||||||
#[case] prompt_block: ActiveRadarrBlock,
|
#[case] prompt_block: ActiveRadarrBlock,
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ mod tests {
|
|||||||
#[values(
|
#[values(
|
||||||
ActiveRadarrBlock::DeleteIndexerPrompt,
|
ActiveRadarrBlock::DeleteIndexerPrompt,
|
||||||
ActiveRadarrBlock::Indexers,
|
ActiveRadarrBlock::Indexers,
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt,
|
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
|
|||||||
match self.active_radarr_block {
|
match self.active_radarr_block {
|
||||||
ActiveRadarrBlock::DeleteRootFolderPrompt => {
|
ActiveRadarrBlock::DeleteRootFolderPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteRootFolder);
|
self.app.data.radarr_data.prompt_confirm_action =
|
||||||
|
Some(RadarrEvent::DeleteRootFolder(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
@@ -131,7 +132,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
|
|||||||
.text
|
.text
|
||||||
.is_empty() =>
|
.is_empty() =>
|
||||||
{
|
{
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddRootFolder);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddRootFolder(None));
|
||||||
self.app.data.radarr_data.prompt_confirm = true;
|
self.app.data.radarr_data.prompt_confirm = true;
|
||||||
self.app.should_ignore_quit_key = false;
|
self.app.should_ignore_quit_key = false;
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_home_end {
|
mod test_handle_home_end {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::models::radarr_models::RootFolder;
|
use crate::models::radarr_models::RootFolder;
|
||||||
@@ -132,14 +134,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_root_folder
|
.edit_root_folder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -152,14 +154,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_root_folder
|
.edit_root_folder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -222,6 +224,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod test_handle_left_right_action {
|
mod test_handle_left_right_action {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
@@ -313,14 +317,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_root_folder
|
.edit_root_folder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -333,14 +337,14 @@ mod tests {
|
|||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*app
|
app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.edit_root_folder
|
.edit_root_folder
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow(),
|
.load(Ordering::SeqCst),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -381,7 +385,7 @@ mod tests {
|
|||||||
assert!(!app.should_ignore_quit_key);
|
assert!(!app.should_ignore_quit_key);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::AddRootFolder)
|
Some(RadarrEvent::AddRootFolder(None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
@@ -438,7 +442,7 @@ mod tests {
|
|||||||
assert!(app.data.radarr_data.prompt_confirm);
|
assert!(app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::DeleteRootFolder)
|
Some(RadarrEvent::DeleteRootFolder(None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
|
|||||||
}
|
}
|
||||||
ActiveRadarrBlock::SystemTaskStartConfirmPrompt => {
|
ActiveRadarrBlock::SystemTaskStartConfirmPrompt => {
|
||||||
if self.app.data.radarr_data.prompt_confirm {
|
if self.app.data.radarr_data.prompt_confirm {
|
||||||
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask);
|
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app.pop_navigation_stack();
|
self.app.pop_navigation_stack();
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ mod tests {
|
|||||||
assert!(app.data.radarr_data.prompt_confirm);
|
assert!(app.data.radarr_data.prompt_confirm);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.radarr_data.prompt_confirm_action,
|
app.data.radarr_data.prompt_confirm_action,
|
||||||
Some(RadarrEvent::StartTask)
|
Some(RadarrEvent::StartTask(None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.get_current_route(),
|
app.get_current_route(),
|
||||||
|
|||||||
+85
-11
@@ -1,14 +1,23 @@
|
|||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
use std::panic::PanicInfo;
|
use std::panic::PanicHookInfo;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{io, panic};
|
use std::{io, panic, process};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use clap::{
|
||||||
|
command, crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser,
|
||||||
|
};
|
||||||
|
use clap_complete::generate;
|
||||||
|
use colored::Colorize;
|
||||||
use crossterm::execute;
|
use crossterm::execute;
|
||||||
use crossterm::terminal::{
|
use crossterm::terminal::{
|
||||||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
disable_raw_mode, enable_raw_mode, size, EnterAlternateScreen, LeaveAlternateScreen,
|
||||||
};
|
};
|
||||||
|
use log::error;
|
||||||
|
use network::NetworkTrait;
|
||||||
use ratatui::backend::CrosstermBackend;
|
use ratatui::backend::CrosstermBackend;
|
||||||
use ratatui::Terminal;
|
use ratatui::Terminal;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
@@ -16,12 +25,14 @@ use tokio::sync::{mpsc, Mutex};
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
use crate::cli::Command;
|
||||||
use crate::event::input_event::{Events, InputEvent};
|
use crate::event::input_event::{Events, InputEvent};
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::network::{Network, NetworkEvent};
|
use crate::network::{Network, NetworkEvent};
|
||||||
use crate::ui::ui;
|
use crate::ui::ui;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
|
mod cli;
|
||||||
mod event;
|
mod event;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod logos;
|
mod logos;
|
||||||
@@ -30,16 +41,49 @@ mod network;
|
|||||||
mod ui;
|
mod ui;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
static MIN_TERM_WIDTH: u16 = 205;
|
||||||
|
static MIN_TERM_HEIGHT: u16 = 40;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(
|
||||||
|
name = crate_name!(),
|
||||||
|
author = crate_authors!(),
|
||||||
|
version = crate_version!(),
|
||||||
|
about = crate_description!(),
|
||||||
|
help_template = "\
|
||||||
|
{before-help}{name} {version}
|
||||||
|
{author-with-newline}
|
||||||
|
{about-with-newline}
|
||||||
|
{usage-heading} {usage}
|
||||||
|
|
||||||
|
{all-args}{after-help}
|
||||||
|
"
|
||||||
|
)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Option<Command>,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
log4rs::init_config(utils::init_logging_config())?;
|
log4rs::init_config(utils::init_logging_config())?;
|
||||||
panic::set_hook(Box::new(|info| {
|
panic::set_hook(Box::new(|info| {
|
||||||
panic_hook(info);
|
panic_hook(info);
|
||||||
}));
|
}));
|
||||||
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
let r = running.clone();
|
||||||
|
let args = Cli::parse();
|
||||||
let config = confy::load("managarr", "config")?;
|
let config = confy::load("managarr", "config")?;
|
||||||
let (sync_network_tx, sync_network_rx) = mpsc::channel(500);
|
let (sync_network_tx, sync_network_rx) = mpsc::channel(500);
|
||||||
let cancellation_token = CancellationToken::new();
|
let cancellation_token = CancellationToken::new();
|
||||||
|
let ctrlc_cancellation_token = cancellation_token.clone();
|
||||||
|
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
ctrlc_cancellation_token.cancel();
|
||||||
|
r.store(false, Ordering::SeqCst);
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
|
||||||
let app = Arc::new(Mutex::new(App::new(
|
let app = Arc::new(Mutex::new(App::new(
|
||||||
sync_network_tx,
|
sync_network_tx,
|
||||||
@@ -47,11 +91,28 @@ async fn main() -> Result<()> {
|
|||||||
cancellation_token.clone(),
|
cancellation_token.clone(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let app_nw = Arc::clone(&app);
|
match args.command {
|
||||||
|
Some(command) => match command {
|
||||||
|
Command::Radarr(_) => {
|
||||||
|
let app_nw = Arc::clone(&app);
|
||||||
|
let mut network = Network::new(&app_nw, cancellation_token);
|
||||||
|
|
||||||
std::thread::spawn(move || start_networking(sync_network_rx, &app_nw, cancellation_token));
|
if let Err(e) = cli::handle_command(&app, command, &mut network).await {
|
||||||
|
eprintln!("error: {}", e.to_string().red());
|
||||||
start_ui(&app).await?;
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Completions { shell } => {
|
||||||
|
let mut cli = Cli::command();
|
||||||
|
generate(shell, &mut cli, "managarr", &mut io::stdout())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let app_nw = Arc::clone(&app);
|
||||||
|
std::thread::spawn(move || start_networking(sync_network_rx, &app_nw, cancellation_token));
|
||||||
|
start_ui(&app).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -65,11 +126,24 @@ async fn start_networking(
|
|||||||
let mut network = Network::new(app, cancellation_token);
|
let mut network = Network::new(app, cancellation_token);
|
||||||
|
|
||||||
while let Some(network_event) = network_rx.recv().await {
|
while let Some(network_event) = network_rx.recv().await {
|
||||||
network.handle_network_event(network_event).await;
|
if let Err(e) = network.handle_network_event(network_event).await {
|
||||||
|
error!("Encountered an error handling network event: {e:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_ui(app: &Arc<Mutex<App<'_>>>) -> Result<()> {
|
async fn start_ui(app: &Arc<Mutex<App<'_>>>) -> Result<()> {
|
||||||
|
let (width, height) = size()?;
|
||||||
|
if width < MIN_TERM_WIDTH || height < MIN_TERM_HEIGHT {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Terminal too small. Minimum size required: {}x{}; current terminal size: {}x{}",
|
||||||
|
MIN_TERM_WIDTH,
|
||||||
|
MIN_TERM_HEIGHT,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
@@ -111,7 +185,7 @@ async fn start_ui(app: &Arc<Mutex<App<'_>>>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn panic_hook(info: &PanicInfo<'_>) {
|
fn panic_hook(info: &PanicHookInfo<'_>) {
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use crossterm::style::Print;
|
use crossterm::style::Print;
|
||||||
|
|
||||||
@@ -139,7 +213,7 @@ fn panic_hook(info: &PanicInfo<'_>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn panic_hook(info: &PanicInfo<'_>) {
|
fn panic_hook(info: &PanicHookInfo<'_>) {
|
||||||
use human_panic::{handle_dump, print_msg, Metadata};
|
use human_panic::{handle_dump, print_msg, Metadata};
|
||||||
|
|
||||||
let meta = Metadata {
|
let meta = Metadata {
|
||||||
|
|||||||
+60
-20
@@ -1,11 +1,11 @@
|
|||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
|
use radarr_models::RadarrSerdeable;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
|
|
||||||
pub mod radarr_models;
|
pub mod radarr_models;
|
||||||
pub mod servarr_data;
|
pub mod servarr_data;
|
||||||
pub mod stateful_list;
|
pub mod stateful_list;
|
||||||
@@ -29,6 +29,12 @@ pub enum Route {
|
|||||||
Tautulli,
|
Tautulli,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Serdeable {
|
||||||
|
Radarr(RadarrSerdeable),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Scrollable {
|
pub trait Scrollable {
|
||||||
fn scroll_down(&mut self);
|
fn scroll_down(&mut self);
|
||||||
fn scroll_up(&mut self);
|
fn scroll_up(&mut self);
|
||||||
@@ -88,19 +94,42 @@ impl Scrollable for ScrollableText {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Deserialize, Debug)]
|
||||||
#[serde(from = "String")]
|
#[serde(from = "String")]
|
||||||
pub struct HorizontallyScrollableText {
|
pub struct HorizontallyScrollableText {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub offset: RefCell<usize>,
|
pub offset: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for HorizontallyScrollableText {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
HorizontallyScrollableText {
|
||||||
|
text: self.text.clone(),
|
||||||
|
offset: AtomicUsize::new(self.offset.load(Ordering::SeqCst)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for HorizontallyScrollableText {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.text == other.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for HorizontallyScrollableText {}
|
||||||
|
|
||||||
impl From<String> for HorizontallyScrollableText {
|
impl From<String> for HorizontallyScrollableText {
|
||||||
fn from(text: String) -> HorizontallyScrollableText {
|
fn from(text: String) -> HorizontallyScrollableText {
|
||||||
HorizontallyScrollableText::new(text)
|
HorizontallyScrollableText::new(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&String> for HorizontallyScrollableText {
|
||||||
|
fn from(text: &String) -> HorizontallyScrollableText {
|
||||||
|
HorizontallyScrollableText::new(text.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for HorizontallyScrollableText {
|
impl From<&str> for HorizontallyScrollableText {
|
||||||
fn from(text: &str) -> HorizontallyScrollableText {
|
fn from(text: &str) -> HorizontallyScrollableText {
|
||||||
HorizontallyScrollableText::new(text.to_owned())
|
HorizontallyScrollableText::new(text.to_owned())
|
||||||
@@ -109,14 +138,14 @@ impl From<&str> for HorizontallyScrollableText {
|
|||||||
|
|
||||||
impl Display for HorizontallyScrollableText {
|
impl Display for HorizontallyScrollableText {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
if *self.offset.borrow() == 0 {
|
if self.offset.load(Ordering::SeqCst) == 0 {
|
||||||
write!(f, "{}", self.text)
|
write!(f, "{}", self.text)
|
||||||
} else {
|
} else {
|
||||||
let text_vec = self.text.chars().collect::<Vec<_>>();
|
let text_vec = self.text.chars().collect::<Vec<_>>();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
text_vec[*self.offset.borrow()..]
|
text_vec[self.offset.load(Ordering::SeqCst)..]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
@@ -138,7 +167,7 @@ impl HorizontallyScrollableText {
|
|||||||
pub fn new(text: String) -> HorizontallyScrollableText {
|
pub fn new(text: String) -> HorizontallyScrollableText {
|
||||||
HorizontallyScrollableText {
|
HorizontallyScrollableText {
|
||||||
text,
|
text,
|
||||||
offset: RefCell::new(0),
|
offset: AtomicUsize::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,46 +176,44 @@ impl HorizontallyScrollableText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_left(&self) {
|
pub fn scroll_left(&self) {
|
||||||
if *self.offset.borrow() < self.len() {
|
if self.offset.load(Ordering::SeqCst) < self.len() {
|
||||||
let new_offset = *self.offset.borrow() + 1;
|
self.offset.fetch_add(1, Ordering::SeqCst);
|
||||||
*self.offset.borrow_mut() = new_offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_right(&self) {
|
pub fn scroll_right(&self) {
|
||||||
if *self.offset.borrow() > 0 {
|
if self.offset.load(Ordering::SeqCst) > 0 {
|
||||||
let new_offset = *self.offset.borrow() - 1;
|
self.offset.fetch_sub(1, Ordering::SeqCst);
|
||||||
*self.offset.borrow_mut() = new_offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_home(&self) {
|
pub fn scroll_home(&self) {
|
||||||
*self.offset.borrow_mut() = self.len();
|
self.offset.store(self.len(), Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_offset(&self) {
|
pub fn reset_offset(&self) {
|
||||||
*self.offset.borrow_mut() = 0;
|
self.offset.store(0, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_left_or_reset(&self, width: usize, is_current_selection: bool, can_scroll: bool) {
|
pub fn scroll_left_or_reset(&self, width: usize, is_current_selection: bool, can_scroll: bool) {
|
||||||
if can_scroll && is_current_selection && self.len() >= width {
|
if can_scroll && is_current_selection && self.len() >= width {
|
||||||
if *self.offset.borrow() < self.len() {
|
if self.offset.load(Ordering::SeqCst) < self.len() {
|
||||||
self.scroll_left();
|
self.scroll_left();
|
||||||
} else {
|
} else {
|
||||||
self.reset_offset();
|
self.reset_offset();
|
||||||
}
|
}
|
||||||
} else if *self.offset.borrow() != 0 && !is_current_selection {
|
} else if self.offset.load(Ordering::SeqCst) != 0 && !is_current_selection {
|
||||||
self.reset_offset();
|
self.reset_offset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) {
|
pub fn pop(&mut self) {
|
||||||
if *self.offset.borrow() < self.len() {
|
if self.offset.load(Ordering::SeqCst) < self.len() {
|
||||||
let (index, _) = self
|
let (index, _) = self
|
||||||
.text
|
.text
|
||||||
.chars()
|
.chars()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.nth(self.len() - *self.offset.borrow() - 1)
|
.nth(self.len() - self.offset.load(Ordering::SeqCst) - 1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.text = self
|
self.text = self
|
||||||
.text
|
.text
|
||||||
@@ -202,7 +229,7 @@ impl HorizontallyScrollableText {
|
|||||||
if self.text.is_empty() {
|
if self.text.is_empty() {
|
||||||
self.text.push(character);
|
self.text.push(character);
|
||||||
} else {
|
} else {
|
||||||
let index = self.len() - *self.offset.borrow();
|
let index = self.len() - self.offset.load(Ordering::SeqCst);
|
||||||
|
|
||||||
if index == self.len() {
|
if index == self.len() {
|
||||||
self.text.push(character);
|
self.text.push(character);
|
||||||
@@ -338,3 +365,16 @@ pub fn strip_non_search_characters(input: &str) -> String {
|
|||||||
.replace_all(&input.to_lowercase(), "")
|
.replace_all(&input.to_lowercase(), "")
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! serde_enum_from {
|
||||||
|
($enum_name:ident { $($variant:ident($ty:ty),)* }) => {
|
||||||
|
$(
|
||||||
|
impl From<$ty> for $enum_name {
|
||||||
|
fn from(value: $ty) -> Self {
|
||||||
|
$enum_name::$variant(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+151
-40
@@ -1,6 +1,7 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::cell::RefCell;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use serde::de::value::Error as ValueError;
|
use serde::de::value::Error as ValueError;
|
||||||
@@ -100,7 +101,22 @@ mod tests {
|
|||||||
let test_text = "Test string";
|
let test_text = "Test string";
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text.to_owned());
|
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text.to_owned());
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_horizontally_scrollable_text_from_string_ref() {
|
||||||
|
let test_text = "Test string".to_owned();
|
||||||
|
let horizontally_scrollable_text = HorizontallyScrollableText::from(&test_text);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +125,10 @@ mod tests {
|
|||||||
let test_text = "Test string";
|
let test_text = "Test string";
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
let horizontally_scrollable_text = HorizontallyScrollableText::from(test_text);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,14 +141,14 @@ mod tests {
|
|||||||
|
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText {
|
let horizontally_scrollable_text = HorizontallyScrollableText {
|
||||||
text: test_text.to_owned(),
|
text: test_text.to_owned(),
|
||||||
offset: RefCell::new(test_text.len() - 1),
|
offset: AtomicUsize::new(test_text.len() - 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.to_string(), "g");
|
assert_str_eq!(horizontally_scrollable_text.to_string(), "g");
|
||||||
|
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText {
|
let horizontally_scrollable_text = HorizontallyScrollableText {
|
||||||
text: test_text.to_owned(),
|
text: test_text.to_owned(),
|
||||||
offset: RefCell::new(test_text.len()),
|
offset: AtomicUsize::new(test_text.len()),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(horizontally_scrollable_text.to_string().is_empty());
|
assert!(horizontally_scrollable_text.to_string().is_empty());
|
||||||
@@ -140,7 +159,10 @@ mod tests {
|
|||||||
let test_text = "Test string";
|
let test_text = "Test string";
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::new(test_text.to_owned());
|
let horizontally_scrollable_text = HorizontallyScrollableText::new(test_text.to_owned());
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
assert_str_eq!(horizontally_scrollable_text.text, test_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,18 +180,24 @@ mod tests {
|
|||||||
fn test_horizontally_scrollable_text_scroll_text_left() {
|
fn test_horizontally_scrollable_text_scroll_text_left() {
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
for i in 1..horizontally_scrollable_text.text.len() - 1 {
|
for i in 1..horizontally_scrollable_text.text.len() - 1 {
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), i);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
i
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*horizontally_scrollable_text.offset.borrow(),
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
horizontally_scrollable_text.text.len() - 1
|
horizontally_scrollable_text.text.len() - 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -180,37 +208,51 @@ mod tests {
|
|||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
assert_str_eq!(horizontally_scrollable_text.to_string(), "리");
|
assert_str_eq!(horizontally_scrollable_text.to_string(), "리");
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
2
|
||||||
|
);
|
||||||
assert_str_eq!(horizontally_scrollable_text.to_string(), "");
|
assert_str_eq!(horizontally_scrollable_text.to_string(), "");
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
2
|
||||||
|
);
|
||||||
assert!(horizontally_scrollable_text.to_string().is_empty());
|
assert!(horizontally_scrollable_text.to_string().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_scroll_text_right() {
|
fn test_horizontally_scrollable_text_scroll_text_right() {
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
let horizontally_scrollable_text = HorizontallyScrollableText::from("Test string");
|
||||||
*horizontally_scrollable_text.offset.borrow_mut() = horizontally_scrollable_text.text.len();
|
horizontally_scrollable_text
|
||||||
|
.offset
|
||||||
|
.store(horizontally_scrollable_text.len(), Ordering::SeqCst);
|
||||||
|
|
||||||
for i in 1..horizontally_scrollable_text.text.len() {
|
for i in 1..horizontally_scrollable_text.text.len() {
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*horizontally_scrollable_text.offset.borrow(),
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
horizontally_scrollable_text.text.len() - i
|
horizontally_scrollable_text.text.len() - i
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -220,7 +262,7 @@ mod tests {
|
|||||||
horizontally_scrollable_text.scroll_home();
|
horizontally_scrollable_text.scroll_home();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*horizontally_scrollable_text.offset.borrow(),
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
horizontally_scrollable_text.text.len()
|
horizontally_scrollable_text.text.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -231,19 +273,25 @@ mod tests {
|
|||||||
|
|
||||||
horizontally_scrollable_text.scroll_home();
|
horizontally_scrollable_text.scroll_home();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_horizontally_scrollable_text_reset_offset() {
|
fn test_horizontally_scrollable_text_reset_offset() {
|
||||||
let horizontally_scrollable_text = HorizontallyScrollableText {
|
let horizontally_scrollable_text = HorizontallyScrollableText {
|
||||||
text: "Test string".to_owned(),
|
text: "Test string".to_owned(),
|
||||||
offset: RefCell::new(1),
|
offset: AtomicUsize::new(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
horizontally_scrollable_text.reset_offset();
|
horizontally_scrollable_text.reset_offset();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -254,23 +302,38 @@ mod tests {
|
|||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, false, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, false, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, false);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, false);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(test_text.len(), false, true);
|
horizontally_scrollable_text.scroll_left_or_reset(test_text.len(), false, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -278,11 +341,17 @@ mod tests {
|
|||||||
let horizontally_scrollable_test = HorizontallyScrollableText::from("Test string");
|
let horizontally_scrollable_test = HorizontallyScrollableText::from("Test string");
|
||||||
horizontally_scrollable_test.scroll_left();
|
horizontally_scrollable_test.scroll_left();
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_test.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_test.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_test.scroll_left_or_reset(3, false, false);
|
horizontally_scrollable_test.scroll_left_or_reset(3, false, false);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_test.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_test.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -292,15 +361,24 @@ mod tests {
|
|||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
horizontally_scrollable_text.scroll_left_or_reset(width, true, true);
|
||||||
|
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -310,32 +388,47 @@ mod tests {
|
|||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin우g");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin우g");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test sTring");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTring");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_home();
|
horizontally_scrollable_text.scroll_home();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test sTrin");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 10);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "est sTrin");
|
assert_str_eq!(horizontally_scrollable_text.text, "est sTrin");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 9);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
9
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -344,17 +437,26 @@ mod tests {
|
|||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "우");
|
assert_str_eq!(horizontally_scrollable_text.text, "우");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert!(horizontally_scrollable_text.text.is_empty());
|
assert!(horizontally_scrollable_text.text.is_empty());
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.pop();
|
horizontally_scrollable_text.pop();
|
||||||
|
|
||||||
assert!(horizontally_scrollable_text.text.is_empty());
|
assert!(horizontally_scrollable_text.text.is_empty());
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -364,20 +466,29 @@ mod tests {
|
|||||||
horizontally_scrollable_text.push('h');
|
horizontally_scrollable_text.push('h');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우ngh");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우ngh");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_left();
|
horizontally_scrollable_text.scroll_left();
|
||||||
horizontally_scrollable_text.push('l');
|
horizontally_scrollable_text.push('l');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 1);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.scroll_right();
|
horizontally_scrollable_text.scroll_right();
|
||||||
horizontally_scrollable_text.push('리');
|
horizontally_scrollable_text.push('리');
|
||||||
|
|
||||||
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh리");
|
assert_str_eq!(horizontally_scrollable_text.text, "Test stri우nglh리");
|
||||||
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
|
assert_eq!(
|
||||||
|
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+199
-43
@@ -1,18 +1,21 @@
|
|||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use clap::ValueEnum;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{json, Number, Value};
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::{models::HorizontallyScrollableText, serde_enum_from};
|
||||||
|
|
||||||
|
use super::Serdeable;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "radarr_models_tests.rs"]
|
#[path = "radarr_models_tests.rs"]
|
||||||
mod radarr_models_tests;
|
mod radarr_models_tests;
|
||||||
|
|
||||||
#[derive(Default, Serialize, Debug)]
|
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddMovieBody {
|
pub struct AddMovieBody {
|
||||||
pub tmdb_id: i64,
|
pub tmdb_id: i64,
|
||||||
@@ -25,7 +28,7 @@ pub struct AddMovieBody {
|
|||||||
pub add_options: AddOptions,
|
pub add_options: AddOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddMovieSearchResult {
|
pub struct AddMovieSearchResult {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -42,7 +45,7 @@ pub struct AddMovieSearchResult {
|
|||||||
pub ratings: RatingsList,
|
pub ratings: RatingsList,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Debug, PartialEq, Eq)]
|
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AddOptions {
|
pub struct AddOptions {
|
||||||
pub monitor: String,
|
pub monitor: String,
|
||||||
@@ -54,12 +57,12 @@ pub struct AddRootFolderBody {
|
|||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct BlocklistResponse {
|
pub struct BlocklistResponse {
|
||||||
pub records: Vec<BlocklistItem>,
|
pub records: Vec<BlocklistItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BlocklistItem {
|
pub struct BlocklistItem {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -77,12 +80,12 @@ pub struct BlocklistItem {
|
|||||||
pub movie: BlocklistItemMovie,
|
pub movie: BlocklistItemMovie,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct BlocklistItemMovie {
|
pub struct BlocklistItemMovie {
|
||||||
pub title: HorizontallyScrollableText,
|
pub title: HorizontallyScrollableText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Collection {
|
pub struct Collection {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -99,7 +102,7 @@ pub struct Collection {
|
|||||||
pub movies: Option<Vec<CollectionMovie>>,
|
pub movies: Option<Vec<CollectionMovie>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CollectionMovie {
|
pub struct CollectionMovie {
|
||||||
pub title: HorizontallyScrollableText,
|
pub title: HorizontallyScrollableText,
|
||||||
@@ -120,7 +123,7 @@ pub struct CommandBody {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Credit {
|
pub struct Credit {
|
||||||
pub person_name: String,
|
pub person_name: String,
|
||||||
@@ -131,7 +134,7 @@ pub struct Credit {
|
|||||||
pub credit_type: CreditType,
|
pub credit_type: CreditType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, PartialEq, Eq, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Debug)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum CreditType {
|
pub enum CreditType {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -139,7 +142,15 @@ pub enum CreditType {
|
|||||||
Crew,
|
Crew,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub struct DeleteMovieParams {
|
||||||
|
pub id: i64,
|
||||||
|
pub delete_movie_files: bool,
|
||||||
|
pub add_list_exclusion: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DiskSpace {
|
pub struct DiskSpace {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -148,7 +159,7 @@ pub struct DiskSpace {
|
|||||||
pub total_space: i64,
|
pub total_space: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DownloadRecord {
|
pub struct DownloadRecord {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@@ -167,12 +178,51 @@ pub struct DownloadRecord {
|
|||||||
pub download_client: String,
|
pub download_client: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug)]
|
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DownloadsResponse {
|
pub struct DownloadsResponse {
|
||||||
pub records: Vec<DownloadRecord>,
|
pub records: Vec<DownloadRecord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct EditCollectionParams {
|
||||||
|
pub collection_id: i64,
|
||||||
|
pub monitored: Option<bool>,
|
||||||
|
pub minimum_availability: Option<MinimumAvailability>,
|
||||||
|
pub quality_profile_id: Option<i64>,
|
||||||
|
pub root_folder_path: Option<String>,
|
||||||
|
pub search_on_add: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct EditIndexerParams {
|
||||||
|
pub indexer_id: i64,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub enable_rss: Option<bool>,
|
||||||
|
pub enable_automatic_search: Option<bool>,
|
||||||
|
pub enable_interactive_search: Option<bool>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub api_key: Option<String>,
|
||||||
|
pub seed_ratio: Option<String>,
|
||||||
|
pub tags: Option<Vec<i64>>,
|
||||||
|
pub priority: Option<i64>,
|
||||||
|
pub clear_tags: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct EditMovieParams {
|
||||||
|
pub movie_id: i64,
|
||||||
|
pub monitored: Option<bool>,
|
||||||
|
pub minimum_availability: Option<MinimumAvailability>,
|
||||||
|
pub quality_profile_id: Option<i64>,
|
||||||
|
pub root_folder_path: Option<String>,
|
||||||
|
pub tags: Option<Vec<i64>>,
|
||||||
|
pub clear_tags: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Deserialize, Serialize, Debug, Clone, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Indexer {
|
pub struct Indexer {
|
||||||
@@ -223,7 +273,7 @@ pub struct IndexerSettings {
|
|||||||
pub whitelisted_hardcoded_subs: HorizontallyScrollableText,
|
pub whitelisted_hardcoded_subs: HorizontallyScrollableText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct IndexerTestResult {
|
pub struct IndexerTestResult {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -232,7 +282,7 @@ pub struct IndexerTestResult {
|
|||||||
pub validation_failures: Vec<IndexerValidationFailure>,
|
pub validation_failures: Vec<IndexerValidationFailure>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct IndexerValidationFailure {
|
pub struct IndexerValidationFailure {
|
||||||
pub property_name: String,
|
pub property_name: String,
|
||||||
@@ -240,12 +290,12 @@ pub struct IndexerValidationFailure {
|
|||||||
pub severity: String,
|
pub severity: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||||
pub struct Language {
|
pub struct Language {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Default, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Log {
|
pub struct Log {
|
||||||
pub time: DateTime<Utc>,
|
pub time: DateTime<Utc>,
|
||||||
@@ -257,12 +307,12 @@ pub struct Log {
|
|||||||
pub method: Option<String>,
|
pub method: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Default, Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct LogResponse {
|
pub struct LogResponse {
|
||||||
pub records: Vec<Log>,
|
pub records: Vec<Log>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
|
||||||
#[derivative(Default)]
|
#[derivative(Default)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MediaInfo {
|
pub struct MediaInfo {
|
||||||
@@ -286,7 +336,9 @@ pub struct MediaInfo {
|
|||||||
pub scan_type: String,
|
pub scan_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter, ValueEnum,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum MinimumAvailability {
|
pub enum MinimumAvailability {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -319,7 +371,7 @@ impl MinimumAvailability {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter)]
|
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, EnumIter, ValueEnum)]
|
||||||
pub enum Monitor {
|
pub enum Monitor {
|
||||||
#[default]
|
#[default]
|
||||||
MovieOnly,
|
MovieOnly,
|
||||||
@@ -348,7 +400,7 @@ impl Monitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Movie {
|
pub struct Movie {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -380,7 +432,7 @@ pub struct Movie {
|
|||||||
pub collection: Option<MovieCollection>,
|
pub collection: Option<MovieCollection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MovieCollection {
|
pub struct MovieCollection {
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
@@ -393,7 +445,7 @@ pub struct MovieCommandBody {
|
|||||||
pub movie_ids: Vec<i64>,
|
pub movie_ids: Vec<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MovieFile {
|
pub struct MovieFile {
|
||||||
pub relative_path: String,
|
pub relative_path: String,
|
||||||
@@ -402,7 +454,7 @@ pub struct MovieFile {
|
|||||||
pub media_info: Option<MediaInfo>,
|
pub media_info: Option<MediaInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MovieHistoryItem {
|
pub struct MovieHistoryItem {
|
||||||
pub source_title: HorizontallyScrollableText,
|
pub source_title: HorizontallyScrollableText,
|
||||||
@@ -412,24 +464,33 @@ pub struct MovieHistoryItem {
|
|||||||
pub event_type: String,
|
pub event_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||||
pub struct Quality {
|
pub struct Quality {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug)]
|
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub struct QualityProfile {
|
pub struct QualityProfile {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
impl From<(&i64, &String)> for QualityProfile {
|
||||||
|
fn from(value: (&i64, &String)) -> Self {
|
||||||
|
QualityProfile {
|
||||||
|
id: *value.0,
|
||||||
|
name: value.1.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||||
pub struct QualityWrapper {
|
pub struct QualityWrapper {
|
||||||
pub quality: Quality,
|
pub quality: Quality,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct QueueEvent {
|
pub struct QueueEvent {
|
||||||
pub trigger: String,
|
pub trigger: String,
|
||||||
@@ -442,14 +503,14 @@ pub struct QueueEvent {
|
|||||||
pub duration: Option<String>,
|
pub duration: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Derivative, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[derivative(Default)]
|
#[derivative(Default)]
|
||||||
pub struct Rating {
|
pub struct Rating {
|
||||||
#[derivative(Default(value = "Number::from(0)"))]
|
#[derivative(Default(value = "Number::from(0)"))]
|
||||||
pub value: Number,
|
pub value: Number,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RatingsList {
|
pub struct RatingsList {
|
||||||
pub imdb: Option<Rating>,
|
pub imdb: Option<Rating>,
|
||||||
@@ -457,7 +518,7 @@ pub struct RatingsList {
|
|||||||
pub rotten_tomatoes: Option<Rating>,
|
pub rotten_tomatoes: Option<Rating>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Release {
|
pub struct Release {
|
||||||
@@ -479,7 +540,7 @@ pub struct Release {
|
|||||||
pub quality: QualityWrapper,
|
pub quality: QualityWrapper,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Debug)]
|
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ReleaseDownloadBody {
|
pub struct ReleaseDownloadBody {
|
||||||
pub guid: String,
|
pub guid: String,
|
||||||
@@ -487,7 +548,7 @@ pub struct ReleaseDownloadBody {
|
|||||||
pub movie_id: i64,
|
pub movie_id: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RootFolder {
|
pub struct RootFolder {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
@@ -499,25 +560,25 @@ pub struct RootFolder {
|
|||||||
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
|
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SystemStatus {
|
pub struct SystemStatus {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub start_time: DateTime<Utc>,
|
pub start_time: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug)]
|
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub task_name: String,
|
pub task_name: TaskName,
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub interval: i64,
|
pub interval: i64,
|
||||||
pub last_execution: DateTime<Utc>,
|
pub last_execution: DateTime<Utc>,
|
||||||
@@ -525,13 +586,39 @@ pub struct Task {
|
|||||||
pub next_execution: DateTime<Utc>,
|
pub next_execution: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy, ValueEnum)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub enum TaskName {
|
||||||
|
#[default]
|
||||||
|
ApplicationCheckUpdate,
|
||||||
|
Backup,
|
||||||
|
CheckHealth,
|
||||||
|
CleanUpRecycleBin,
|
||||||
|
Housekeeping,
|
||||||
|
ImportListSync,
|
||||||
|
MessagingCleanup,
|
||||||
|
RefreshCollections,
|
||||||
|
RefreshMonitoredDownloads,
|
||||||
|
RefreshMovie,
|
||||||
|
RssSync,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TaskName {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let task_name = serde_json::to_string(&self)
|
||||||
|
.expect("Unable to serialize task name")
|
||||||
|
.replace('"', "");
|
||||||
|
write!(f, "{task_name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct UnmappedFolder {
|
pub struct UnmappedFolder {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Update {
|
pub struct Update {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@@ -542,9 +629,78 @@ pub struct Update {
|
|||||||
pub changes: UpdateChanges,
|
pub changes: UpdateChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdateChanges {
|
pub struct UpdateChanges {
|
||||||
pub new: Option<Vec<String>>,
|
pub new: Option<Vec<String>>,
|
||||||
pub fixed: Option<Vec<String>>,
|
pub fixed: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum RadarrSerdeable {
|
||||||
|
Value(Value),
|
||||||
|
Tag(Tag),
|
||||||
|
BlocklistResponse(BlocklistResponse),
|
||||||
|
Collections(Vec<Collection>),
|
||||||
|
Credits(Vec<Credit>),
|
||||||
|
DiskSpaces(Vec<DiskSpace>),
|
||||||
|
DownloadsResponse(DownloadsResponse),
|
||||||
|
Indexers(Vec<Indexer>),
|
||||||
|
IndexerSettings(IndexerSettings),
|
||||||
|
LogResponse(LogResponse),
|
||||||
|
Movie(Movie),
|
||||||
|
MovieHistoryItems(Vec<MovieHistoryItem>),
|
||||||
|
Movies(Vec<Movie>),
|
||||||
|
QualityProfiles(Vec<QualityProfile>),
|
||||||
|
QueueEvents(Vec<QueueEvent>),
|
||||||
|
Releases(Vec<Release>),
|
||||||
|
RootFolders(Vec<RootFolder>),
|
||||||
|
SystemStatus(SystemStatus),
|
||||||
|
Tags(Vec<Tag>),
|
||||||
|
Tasks(Vec<Task>),
|
||||||
|
Updates(Vec<Update>),
|
||||||
|
AddMovieSearchResults(Vec<AddMovieSearchResult>),
|
||||||
|
IndexerTestResults(Vec<IndexerTestResult>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RadarrSerdeable> for Serdeable {
|
||||||
|
fn from(value: RadarrSerdeable) -> Serdeable {
|
||||||
|
Serdeable::Radarr(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<()> for RadarrSerdeable {
|
||||||
|
fn from(_: ()) -> Self {
|
||||||
|
RadarrSerdeable::Value(json!({}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_enum_from!(
|
||||||
|
RadarrSerdeable {
|
||||||
|
Value(Value),
|
||||||
|
Tag(Tag),
|
||||||
|
BlocklistResponse(BlocklistResponse),
|
||||||
|
Collections(Vec<Collection>),
|
||||||
|
Credits(Vec<Credit>),
|
||||||
|
DiskSpaces(Vec<DiskSpace>),
|
||||||
|
DownloadsResponse(DownloadsResponse),
|
||||||
|
Indexers(Vec<Indexer>),
|
||||||
|
IndexerSettings(IndexerSettings),
|
||||||
|
LogResponse(LogResponse),
|
||||||
|
Movie(Movie),
|
||||||
|
MovieHistoryItems(Vec<MovieHistoryItem>),
|
||||||
|
Movies(Vec<Movie>),
|
||||||
|
QualityProfiles(Vec<QualityProfile>),
|
||||||
|
QueueEvents(Vec<QueueEvent>),
|
||||||
|
Releases(Vec<Release>),
|
||||||
|
RootFolders(Vec<RootFolder>),
|
||||||
|
SystemStatus(SystemStatus),
|
||||||
|
Tags(Vec<Tag>),
|
||||||
|
Tasks(Vec<Task>),
|
||||||
|
Updates(Vec<Update>),
|
||||||
|
AddMovieSearchResults(Vec<AddMovieSearchResult>),
|
||||||
|
IndexerTestResults(Vec<IndexerTestResult>),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::models::radarr_models::{DownloadRecord, MinimumAvailability, Monitor};
|
use crate::models::{
|
||||||
|
radarr_models::{
|
||||||
|
AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace,
|
||||||
|
DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, Log,
|
||||||
|
LogResponse, MinimumAvailability, Monitor, Movie, MovieHistoryItem, QualityProfile,
|
||||||
|
QueueEvent, RadarrSerdeable, Release, RootFolder, SystemStatus, Tag, Task, TaskName, Update,
|
||||||
|
},
|
||||||
|
Serdeable,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_task_name_display() {
|
||||||
|
assert_str_eq!(
|
||||||
|
TaskName::ApplicationCheckUpdate.to_string(),
|
||||||
|
"ApplicationCheckUpdate"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_minimum_availability_display() {
|
fn test_minimum_availability_display() {
|
||||||
@@ -70,4 +87,323 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(result, expected_record);
|
assert_eq!(result, expected_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from() {
|
||||||
|
let radarr_serdeable = RadarrSerdeable::Value(json!({}));
|
||||||
|
|
||||||
|
let serdeable: Serdeable = Serdeable::from(radarr_serdeable.clone());
|
||||||
|
|
||||||
|
assert_eq!(serdeable, Serdeable::Radarr(radarr_serdeable));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_unit() {
|
||||||
|
let radarr_serdeable = RadarrSerdeable::from(());
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Value(json!({})));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_value() {
|
||||||
|
let value = json!({"test": "test"});
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = value.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_tag() {
|
||||||
|
let tag = Tag {
|
||||||
|
id: 1,
|
||||||
|
..Tag::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = tag.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Tag(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_blocklist_response() {
|
||||||
|
let blocklist_response = BlocklistResponse {
|
||||||
|
records: vec![BlocklistItem {
|
||||||
|
id: 1,
|
||||||
|
..BlocklistItem::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = blocklist_response.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::BlocklistResponse(blocklist_response)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_collections() {
|
||||||
|
let collections = vec![Collection {
|
||||||
|
id: 1,
|
||||||
|
..Collection::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = collections.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Collections(collections));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_credits() {
|
||||||
|
let credits = vec![Credit {
|
||||||
|
person_name: "me".to_owned(),
|
||||||
|
..Credit::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = credits.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Credits(credits));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_disk_spaces() {
|
||||||
|
let disk_spaces = vec![DiskSpace {
|
||||||
|
free_space: 1,
|
||||||
|
total_space: 1,
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = disk_spaces.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::DiskSpaces(disk_spaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_downloads_response() {
|
||||||
|
let downloads_response = DownloadsResponse {
|
||||||
|
records: vec![DownloadRecord {
|
||||||
|
id: 1,
|
||||||
|
..DownloadRecord::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = downloads_response.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::DownloadsResponse(downloads_response)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_indexers() {
|
||||||
|
let indexers = vec![Indexer {
|
||||||
|
id: 1,
|
||||||
|
..Indexer::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = indexers.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Indexers(indexers));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_indexer_settings() {
|
||||||
|
let indexer_settings = IndexerSettings {
|
||||||
|
id: 1,
|
||||||
|
..IndexerSettings::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = indexer_settings.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::IndexerSettings(indexer_settings)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_log_response() {
|
||||||
|
let log_response = LogResponse {
|
||||||
|
records: vec![Log {
|
||||||
|
level: "info".to_owned(),
|
||||||
|
..Log::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = log_response.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::LogResponse(log_response));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_movie() {
|
||||||
|
let movie = Movie {
|
||||||
|
id: 1,
|
||||||
|
..Movie::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = movie.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Movie(movie));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_movie_history_items() {
|
||||||
|
let movie_history_items = vec![MovieHistoryItem {
|
||||||
|
event_type: "test".to_owned(),
|
||||||
|
..MovieHistoryItem::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = movie_history_items.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::MovieHistoryItems(movie_history_items)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_movies() {
|
||||||
|
let movies = vec![Movie {
|
||||||
|
id: 1,
|
||||||
|
..Movie::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = movies.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Movies(movies));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_quality_profiles() {
|
||||||
|
let quality_profiles = vec![QualityProfile {
|
||||||
|
id: 1,
|
||||||
|
..QualityProfile::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = quality_profiles.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::QualityProfiles(quality_profiles)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_queue_events() {
|
||||||
|
let queue_events = vec![QueueEvent {
|
||||||
|
trigger: "test".to_owned(),
|
||||||
|
..QueueEvent::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = queue_events.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::QueueEvents(queue_events));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_releases() {
|
||||||
|
let releases = vec![Release {
|
||||||
|
size: 1,
|
||||||
|
..Release::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = releases.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Releases(releases));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_root_folders() {
|
||||||
|
let root_folders = vec![RootFolder {
|
||||||
|
id: 1,
|
||||||
|
..RootFolder::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = root_folders.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::RootFolders(root_folders));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_system_status() {
|
||||||
|
let system_status = SystemStatus {
|
||||||
|
version: "1".to_owned(),
|
||||||
|
..SystemStatus::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = system_status.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::SystemStatus(system_status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_tags() {
|
||||||
|
let tags = vec![Tag {
|
||||||
|
id: 1,
|
||||||
|
..Tag::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = tags.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Tags(tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_tasks() {
|
||||||
|
let tasks = vec![Task {
|
||||||
|
name: "test".to_owned(),
|
||||||
|
..Task::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = tasks.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Tasks(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_updates() {
|
||||||
|
let updates = vec![Update {
|
||||||
|
version: "test".to_owned(),
|
||||||
|
..Update::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = updates.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(radarr_serdeable, RadarrSerdeable::Updates(updates));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_add_movie_search_results() {
|
||||||
|
let add_movie_search_results = vec![AddMovieSearchResult {
|
||||||
|
tmdb_id: 1,
|
||||||
|
..AddMovieSearchResult::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = add_movie_search_results.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::AddMovieSearchResults(add_movie_search_results)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_radarr_serdeable_from_indexer_test_results() {
|
||||||
|
let indexer_test_results = vec![IndexerTestResult {
|
||||||
|
id: 1,
|
||||||
|
..IndexerTestResult::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let radarr_serdeable: RadarrSerdeable = indexer_test_results.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
radarr_serdeable,
|
||||||
|
RadarrSerdeable::IndexerTestResults(indexer_test_results)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ impl<'a> Default for RadarrData<'a> {
|
|||||||
RadarrData {
|
RadarrData {
|
||||||
root_folders: StatefulTable::default(),
|
root_folders: StatefulTable::default(),
|
||||||
disk_space_vec: Vec::new(),
|
disk_space_vec: Vec::new(),
|
||||||
version: String::default(),
|
version: String::new(),
|
||||||
start_time: DateTime::default(),
|
start_time: DateTime::default(),
|
||||||
movies: StatefulTable::default(),
|
movies: StatefulTable::default(),
|
||||||
selected_block: BlockSelectionState::default(),
|
selected_block: BlockSelectionState::default(),
|
||||||
@@ -269,7 +269,7 @@ pub enum ActiveRadarrBlock {
|
|||||||
FilterMovies,
|
FilterMovies,
|
||||||
FilterMoviesError,
|
FilterMoviesError,
|
||||||
Indexers,
|
Indexers,
|
||||||
IndexerSettingsPrompt,
|
AllIndexerSettingsPrompt,
|
||||||
IndexerSettingsAvailabilityDelayInput,
|
IndexerSettingsAvailabilityDelayInput,
|
||||||
IndexerSettingsConfirmPrompt,
|
IndexerSettingsConfirmPrompt,
|
||||||
IndexerSettingsMaximumSizeInput,
|
IndexerSettingsMaximumSizeInput,
|
||||||
@@ -466,7 +466,7 @@ pub static EDIT_INDEXER_NZB_SELECTION_BLOCKS: [ActiveRadarrBlock; 10] = [
|
|||||||
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
ActiveRadarrBlock::EditIndexerConfirmPrompt,
|
||||||
];
|
];
|
||||||
pub static INDEXER_SETTINGS_BLOCKS: [ActiveRadarrBlock; 10] = [
|
pub static INDEXER_SETTINGS_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||||
ActiveRadarrBlock::IndexerSettingsPrompt,
|
ActiveRadarrBlock::AllIndexerSettingsPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
|
||||||
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
|
||||||
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_indexer_settings_blocks_contents() {
|
fn test_indexer_settings_blocks_contents() {
|
||||||
assert_eq!(INDEXER_SETTINGS_BLOCKS.len(), 10);
|
assert_eq!(INDEXER_SETTINGS_BLOCKS.len(), 10);
|
||||||
assert!(INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::IndexerSettingsPrompt));
|
assert!(INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::AllIndexerSettingsPrompt));
|
||||||
assert!(
|
assert!(
|
||||||
INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput)
|
INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput)
|
||||||
);
|
);
|
||||||
|
|||||||
+41
-16
@@ -1,7 +1,8 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use reqwest::{Client, RequestBuilder};
|
use reqwest::{Client, RequestBuilder};
|
||||||
@@ -13,7 +14,10 @@ use tokio::sync::{Mutex, MutexGuard};
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
|
use crate::models::Serdeable;
|
||||||
use crate::network::radarr_network::RadarrEvent;
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
|
#[cfg(test)]
|
||||||
|
use mockall::automock;
|
||||||
|
|
||||||
pub mod radarr_network;
|
pub mod radarr_network;
|
||||||
mod utils;
|
mod utils;
|
||||||
@@ -22,17 +26,41 @@ mod utils;
|
|||||||
#[path = "network_tests.rs"]
|
#[path = "network_tests.rs"]
|
||||||
mod network_tests;
|
mod network_tests;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub enum NetworkEvent {
|
pub enum NetworkEvent {
|
||||||
Radarr(RadarrEvent),
|
Radarr(RadarrEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, automock)]
|
||||||
|
#[async_trait]
|
||||||
|
pub trait NetworkTrait {
|
||||||
|
async fn handle_network_event(&mut self, network_event: NetworkEvent) -> Result<Serdeable>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Network<'a, 'b> {
|
pub struct Network<'a, 'b> {
|
||||||
client: Client,
|
client: Client,
|
||||||
cancellation_token: CancellationToken,
|
cancellation_token: CancellationToken,
|
||||||
pub app: &'a Arc<Mutex<App<'b>>>,
|
pub app: &'a Arc<Mutex<App<'b>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<'a, 'b> NetworkTrait for Network<'a, 'b> {
|
||||||
|
async fn handle_network_event(&mut self, network_event: NetworkEvent) -> Result<Serdeable> {
|
||||||
|
let resp = match network_event {
|
||||||
|
NetworkEvent::Radarr(radarr_event) => self
|
||||||
|
.handle_radarr_event(radarr_event)
|
||||||
|
.await
|
||||||
|
.map(Serdeable::from),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut app = self.app.lock().await;
|
||||||
|
app.is_loading = false;
|
||||||
|
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Network<'a, 'b> {
|
impl<'a, 'b> Network<'a, 'b> {
|
||||||
pub fn new(app: &'a Arc<Mutex<App<'b>>>, cancellation_token: CancellationToken) -> Self {
|
pub fn new(app: &'a Arc<Mutex<App<'b>>>, cancellation_token: CancellationToken) -> Self {
|
||||||
Network {
|
Network {
|
||||||
@@ -42,22 +70,14 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_network_event(&mut self, network_event: NetworkEvent) {
|
async fn handle_request<B, R>(
|
||||||
match network_event {
|
|
||||||
NetworkEvent::Radarr(radarr_event) => self.handle_radarr_event(radarr_event).await,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut app = self.app.lock().await;
|
|
||||||
app.is_loading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_request<B, R>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
request_props: RequestProps<B>,
|
request_props: RequestProps<B>,
|
||||||
mut app_update_fn: impl FnMut(R, MutexGuard<'_, App<'_>>),
|
mut app_update_fn: impl FnMut(R, MutexGuard<'_, App<'_>>),
|
||||||
) where
|
) -> Result<R>
|
||||||
|
where
|
||||||
B: Serialize + Default + Debug,
|
B: Serialize + Default + Debug,
|
||||||
R: DeserializeOwned,
|
R: DeserializeOwned + Default + Clone,
|
||||||
{
|
{
|
||||||
let ignore_status_code = request_props.ignore_status_code;
|
let ignore_status_code = request_props.ignore_status_code;
|
||||||
let method = request_props.method;
|
let method = request_props.method;
|
||||||
@@ -68,6 +88,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
let mut app = self.app.lock().await;
|
let mut app = self.app.lock().await;
|
||||||
self.cancellation_token = app.reset_cancellation_token();
|
self.cancellation_token = app.reset_cancellation_token();
|
||||||
app.is_loading = false;
|
app.is_loading = false;
|
||||||
|
Ok(R::default())
|
||||||
}
|
}
|
||||||
resp = self.call_api(request_props).await.send() => {
|
resp = self.call_api(request_props).await.send() => {
|
||||||
match resp {
|
match resp {
|
||||||
@@ -78,7 +99,8 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
match utils::parse_response::<R>(response).await {
|
match utils::parse_response::<R>(response).await {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let app = self.app.lock().await;
|
let app = self.app.lock().await;
|
||||||
app_update_fn(value, app);
|
app_update_fn(value.clone(), app);
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to parse response! {e:?}");
|
error!("Failed to parse response! {e:?}");
|
||||||
@@ -87,10 +109,11 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.handle_error(anyhow!("Failed to parse response! {e:?}"));
|
.handle_error(anyhow!("Failed to parse response! {e:?}"));
|
||||||
|
Err(anyhow!("Failed to parse response! {e:?}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RequestMethod::Delete | RequestMethod::Put => (),
|
RequestMethod::Delete | RequestMethod::Put => Ok(R::default()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
@@ -102,6 +125,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
|
|
||||||
error!("Request failed. Received {status} response code with body: {response_body}");
|
error!("Request failed. Received {status} response code with body: {response_body}");
|
||||||
self.app.lock().await.handle_error(anyhow!("Request failed. Received {status} response code with body: {error_body}"));
|
self.app.lock().await.handle_error(anyhow!("Request failed. Received {status} response code with body: {error_body}"));
|
||||||
|
Err(anyhow!("Request failed. Received {status} response code with body: {error_body}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -111,6 +135,7 @@ impl<'a, 'b> Network<'a, 'b> {
|
|||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.handle_error(anyhow!("Failed to send request. {e} "));
|
.handle_error(anyhow!("Failed to send request. {e} "));
|
||||||
|
Err(anyhow!("Failed to send request. {e} "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ mod tests {
|
|||||||
use crate::app::{App, AppConfig, RadarrConfig};
|
use crate::app::{App, AppConfig, RadarrConfig};
|
||||||
use crate::models::HorizontallyScrollableText;
|
use crate::models::HorizontallyScrollableText;
|
||||||
use crate::network::radarr_network::RadarrEvent;
|
use crate::network::radarr_network::RadarrEvent;
|
||||||
use crate::network::{Network, NetworkEvent, RequestMethod, RequestProps};
|
use crate::network::{Network, NetworkEvent, NetworkTrait, RequestMethod, RequestProps};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_network_event_radarr_event() {
|
async fn test_handle_network_event_radarr_event() {
|
||||||
@@ -22,6 +22,7 @@ mod tests {
|
|||||||
let radarr_server = server
|
let radarr_server = server
|
||||||
.mock("GET", "/api/v3/health")
|
.mock("GET", "/api/v3/health")
|
||||||
.with_status(200)
|
.with_status(200)
|
||||||
|
.with_body("{}")
|
||||||
.create_async()
|
.create_async()
|
||||||
.await;
|
.await;
|
||||||
let host = server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned();
|
let host = server.host_with_port().split(':').collect::<Vec<&str>>()[0].to_owned();
|
||||||
@@ -41,7 +42,7 @@ mod tests {
|
|||||||
let app_arc = Arc::new(Mutex::new(app));
|
let app_arc = Arc::new(Mutex::new(app));
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let _ = network
|
||||||
.handle_network_event(RadarrEvent::HealthCheck.into())
|
.handle_network_event(RadarrEvent::HealthCheck.into())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ mod tests {
|
|||||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let _ = network
|
||||||
.handle_request::<Test, ()>(
|
.handle_request::<Test, ()>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -91,7 +92,7 @@ mod tests {
|
|||||||
let (async_server, app_arc, server) = mock_api(request_method, 200, true).await;
|
let (async_server, app_arc, server) = mock_api(request_method, 200, true).await;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -106,6 +107,13 @@ mod tests {
|
|||||||
|
|
||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
assert_str_eq!(app_arc.lock().await.error.text, "Test");
|
assert_str_eq!(app_arc.lock().await.error.text, "Test");
|
||||||
|
assert!(resp.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
resp.unwrap(),
|
||||||
|
Test {
|
||||||
|
value: "Test".to_owned()
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@@ -115,9 +123,9 @@ mod tests {
|
|||||||
) {
|
) {
|
||||||
let (async_server, app_arc, server) = mock_api(request_method, 400, true).await;
|
let (async_server, app_arc, server) = mock_api(request_method, 400, true).await;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
let mut test_result = String::default();
|
let mut test_result = String::new();
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -132,6 +140,13 @@ mod tests {
|
|||||||
|
|
||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
assert!(app_arc.lock().await.error.text.is_empty());
|
assert!(app_arc.lock().await.error.text.is_empty());
|
||||||
|
assert!(resp.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
resp.unwrap(),
|
||||||
|
Test {
|
||||||
|
value: "Test".to_owned()
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -148,7 +163,7 @@ mod tests {
|
|||||||
let mut network = Network::new(&app_arc, cancellation_token);
|
let mut network = Network::new(&app_arc, cancellation_token);
|
||||||
network.cancellation_token.cancel();
|
network.cancellation_token.cancel();
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -164,6 +179,8 @@ mod tests {
|
|||||||
assert!(!async_server.matched_async().await);
|
assert!(!async_server.matched_async().await);
|
||||||
assert!(app_arc.lock().await.error.text.is_empty());
|
assert!(app_arc.lock().await.error.text.is_empty());
|
||||||
assert!(!network.cancellation_token.is_cancelled());
|
assert!(!network.cancellation_token.is_cancelled());
|
||||||
|
assert!(resp.is_ok());
|
||||||
|
assert_eq!(resp.unwrap(), Test::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -179,7 +196,7 @@ mod tests {
|
|||||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -199,6 +216,11 @@ mod tests {
|
|||||||
.error
|
.error
|
||||||
.text
|
.text
|
||||||
.starts_with("Failed to parse response!"));
|
.starts_with("Failed to parse response!"));
|
||||||
|
assert!(resp.is_err());
|
||||||
|
assert!(resp
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.starts_with("Failed to parse response!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -206,10 +228,10 @@ mod tests {
|
|||||||
let app_arc = Arc::new(Mutex::new(App::default()));
|
let app_arc = Arc::new(Mutex::new(App::default()));
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: String::default(),
|
uri: String::new(),
|
||||||
method: RequestMethod::Get,
|
method: RequestMethod::Get,
|
||||||
body: None,
|
body: None,
|
||||||
api_token: "test1234".to_owned(),
|
api_token: "test1234".to_owned(),
|
||||||
@@ -225,6 +247,11 @@ mod tests {
|
|||||||
.error
|
.error
|
||||||
.text
|
.text
|
||||||
.starts_with("Failed to send request."));
|
.starts_with("Failed to send request."));
|
||||||
|
assert!(resp.is_err());
|
||||||
|
assert!(resp
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string()
|
||||||
|
.starts_with("Failed to send request."));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@@ -241,7 +268,7 @@ mod tests {
|
|||||||
let (async_server, app_arc, server) = mock_api(request_method, 404, true).await;
|
let (async_server, app_arc, server) = mock_api(request_method, 404, true).await;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -259,6 +286,11 @@ mod tests {
|
|||||||
app_arc.lock().await.error.text,
|
app_arc.lock().await.error.text,
|
||||||
r#"Request failed. Received 404 Not Found response code with body: { "value": "Test" }"#
|
r#"Request failed. Received 404 Not Found response code with body: { "value": "Test" }"#
|
||||||
);
|
);
|
||||||
|
assert!(resp.is_err());
|
||||||
|
assert_str_eq!(
|
||||||
|
resp.unwrap_err().to_string(),
|
||||||
|
r#"Request failed. Received 404 Not Found response code with body: { "value": "Test" }"#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -266,7 +298,7 @@ mod tests {
|
|||||||
let (async_server, app_arc, server) = mock_api(RequestMethod::Post, 404, false).await;
|
let (async_server, app_arc, server) = mock_api(RequestMethod::Post, 404, false).await;
|
||||||
let mut network = Network::new(&app_arc, CancellationToken::new());
|
let mut network = Network::new(&app_arc, CancellationToken::new());
|
||||||
|
|
||||||
network
|
let resp = network
|
||||||
.handle_request::<(), Test>(
|
.handle_request::<(), Test>(
|
||||||
RequestProps {
|
RequestProps {
|
||||||
uri: format!("{}/test", server.url()),
|
uri: format!("{}/test", server.url()),
|
||||||
@@ -284,6 +316,11 @@ mod tests {
|
|||||||
app_arc.lock().await.error.text,
|
app_arc.lock().await.error.text,
|
||||||
r#"Request failed. Received 404 Not Found response code with body: "#
|
r#"Request failed. Received 404 Not Found response code with body: "#
|
||||||
);
|
);
|
||||||
|
assert!(resp.is_err());
|
||||||
|
assert_str_eq!(
|
||||||
|
resp.unwrap_err().to_string(),
|
||||||
|
r#"Request failed. Received 404 Not Found response code with body: "#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@@ -335,7 +372,7 @@ mod tests {
|
|||||||
async_server.assert_async().await;
|
async_server.assert_async().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
|
||||||
struct Test {
|
struct Test {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|||||||
+912
-516
File diff suppressed because it is too large
Load Diff
+1804
-452
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::style::{Style, Stylize};
|
use ratatui::style::{Style, Stylize};
|
||||||
use ratatui::text::{Line, Text};
|
use ratatui::text::{Line, Text};
|
||||||
@@ -187,7 +189,7 @@ pub fn draw_input_box_popup(
|
|||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
let input_box = InputBox::new(&box_content.text)
|
let input_box = InputBox::new(&box_content.text)
|
||||||
.offset(*box_content.offset.borrow())
|
.offset(box_content.offset.load(Ordering::SeqCst))
|
||||||
.block(title_block_centered(box_title));
|
.block(title_block_centered(box_title));
|
||||||
|
|
||||||
input_box.show_cursor(f, text_box_area);
|
input_box.show_cursor(f, text_box_area);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Layout, Rect};
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::widgets::ListItem;
|
use ratatui::widgets::ListItem;
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
@@ -142,7 +144,7 @@ fn draw_edit_collection_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>
|
|||||||
|
|
||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
let root_folder_input_box = InputBox::new(&path.text)
|
let root_folder_input_box = InputBox::new(&path.text)
|
||||||
.offset(*path.offset.borrow())
|
.offset(path.offset.load(Ordering::SeqCst))
|
||||||
.label("Root Folder")
|
.label("Root Folder")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditCollectionRootFolderPathInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditCollectionRootFolderPathInput);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -79,22 +81,22 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
let name_input_box = InputBox::new(&edit_indexer_modal.name.text)
|
let name_input_box = InputBox::new(&edit_indexer_modal.name.text)
|
||||||
.offset(*edit_indexer_modal.name.offset.borrow())
|
.offset(edit_indexer_modal.name.offset.load(Ordering::SeqCst))
|
||||||
.label("Name")
|
.label("Name")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerNameInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerNameInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerNameInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerNameInput);
|
||||||
let url_input_box = InputBox::new(&edit_indexer_modal.url.text)
|
let url_input_box = InputBox::new(&edit_indexer_modal.url.text)
|
||||||
.offset(*edit_indexer_modal.url.offset.borrow())
|
.offset(edit_indexer_modal.url.offset.load(Ordering::SeqCst))
|
||||||
.label("URL")
|
.label("URL")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerUrlInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerUrlInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerUrlInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerUrlInput);
|
||||||
let api_key_input_box = InputBox::new(&edit_indexer_modal.api_key.text)
|
let api_key_input_box = InputBox::new(&edit_indexer_modal.api_key.text)
|
||||||
.offset(*edit_indexer_modal.api_key.offset.borrow())
|
.offset(edit_indexer_modal.api_key.offset.load(Ordering::SeqCst))
|
||||||
.label("API Key")
|
.label("API Key")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerApiKeyInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerApiKeyInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerApiKeyInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerApiKeyInput);
|
||||||
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
||||||
.offset(*edit_indexer_modal.tags.offset.borrow())
|
.offset(edit_indexer_modal.tags.offset.load(Ordering::SeqCst))
|
||||||
.label("Tags")
|
.label("Tags")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerTagsInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerTagsInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput);
|
||||||
@@ -105,12 +107,12 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
if protocol == "torrent" {
|
if protocol == "torrent" {
|
||||||
let seed_ratio_input_box = InputBox::new(&edit_indexer_modal.seed_ratio.text)
|
let seed_ratio_input_box = InputBox::new(&edit_indexer_modal.seed_ratio.text)
|
||||||
.offset(*edit_indexer_modal.seed_ratio.offset.borrow())
|
.offset(edit_indexer_modal.seed_ratio.offset.load(Ordering::SeqCst))
|
||||||
.label("Seed Ratio")
|
.label("Seed Ratio")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerSeedRatioInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerSeedRatioInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerSeedRatioInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerSeedRatioInput);
|
||||||
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
let tags_input_box = InputBox::new(&edit_indexer_modal.tags.text)
|
||||||
.offset(*edit_indexer_modal.tags.offset.borrow())
|
.offset(edit_indexer_modal.tags.offset.load(Ordering::SeqCst))
|
||||||
.label("Tags")
|
.label("Tags")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerTagsInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditIndexerTagsInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditIndexerTagsInput);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
@@ -114,7 +116,12 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
|||||||
.selected(active_radarr_block == ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::IndexerSettingsRssSyncIntervalInput);
|
||||||
let whitelisted_subs_input_box =
|
let whitelisted_subs_input_box =
|
||||||
InputBox::new(&indexer_settings.whitelisted_hardcoded_subs.text)
|
InputBox::new(&indexer_settings.whitelisted_hardcoded_subs.text)
|
||||||
.offset(*indexer_settings.whitelisted_hardcoded_subs.offset.borrow())
|
.offset(
|
||||||
|
indexer_settings
|
||||||
|
.whitelisted_hardcoded_subs
|
||||||
|
.offset
|
||||||
|
.load(Ordering::SeqCst),
|
||||||
|
)
|
||||||
.label("Whitelisted Subtitle Tags")
|
.label("Whitelisted Subtitle Tags")
|
||||||
.highlighted(
|
.highlighted(
|
||||||
selected_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
selected_block == &ActiveRadarrBlock::IndexerSettingsWhitelistedSubtitleTagsInput,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
@@ -131,14 +133,14 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
let block_content = &app.data.radarr_data.add_movie_search.as_ref().unwrap().text;
|
let block_content = &app.data.radarr_data.add_movie_search.as_ref().unwrap().text;
|
||||||
let offset = *app
|
let offset = app
|
||||||
.data
|
.data
|
||||||
.radarr_data
|
.radarr_data
|
||||||
.add_movie_search
|
.add_movie_search
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.offset
|
.offset
|
||||||
.borrow();
|
.load(Ordering::SeqCst);
|
||||||
let search_results_row_mapping = |movie: &AddMovieSearchResult| {
|
let search_results_row_mapping = |movie: &AddMovieSearchResult| {
|
||||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||||
let imdb_rating = movie
|
let imdb_rating = movie
|
||||||
@@ -416,7 +418,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
|
|
||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
let tags_input_box = InputBox::new(&tags.text)
|
let tags_input_box = InputBox::new(&tags.text)
|
||||||
.offset(*tags.offset.borrow())
|
.offset(tags.offset.load(Ordering::SeqCst))
|
||||||
.label("Tags")
|
.label("Tags")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::AddMovieTagsInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::AddMovieTagsInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::AddMovieTagsInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::AddMovieTagsInput);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Rect};
|
use ratatui::layout::{Constraint, Rect};
|
||||||
use ratatui::prelude::Layout;
|
use ratatui::prelude::Layout;
|
||||||
use ratatui::widgets::ListItem;
|
use ratatui::widgets::ListItem;
|
||||||
@@ -145,12 +147,12 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
|
|
||||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||||
let path_input_box = InputBox::new(&path.text)
|
let path_input_box = InputBox::new(&path.text)
|
||||||
.offset(*path.offset.borrow())
|
.offset(path.offset.load(Ordering::SeqCst))
|
||||||
.label("Path")
|
.label("Path")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditMoviePathInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditMoviePathInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditMoviePathInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditMoviePathInput);
|
||||||
let tags_input_box = InputBox::new(&tags.text)
|
let tags_input_box = InputBox::new(&tags.text)
|
||||||
.offset(*tags.offset.borrow())
|
.offset(tags.offset.load(Ordering::SeqCst))
|
||||||
.label("Tags")
|
.label("Tags")
|
||||||
.highlighted(selected_block == &ActiveRadarrBlock::EditMovieTagsInput)
|
.highlighted(selected_block == &ActiveRadarrBlock::EditMovieTagsInput)
|
||||||
.selected(active_radarr_block == ActiveRadarrBlock::EditMovieTagsInput);
|
.selected(active_radarr_block == ActiveRadarrBlock::EditMovieTagsInput);
|
||||||
|
|||||||
Reference in New Issue
Block a user