Added the full Radarr CLI so users can programmatically access all the same management features as in the TUI

This commit is contained in:
2024-10-29 18:47:40 -06:00
parent 217d3242a8
commit 1f8d72c939
65 changed files with 9401 additions and 1370 deletions
+60 -20
View File
@@ -1,11 +1,11 @@
use std::cell::RefCell;
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use radarr_models::RadarrSerdeable;
use regex::Regex;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Number;
pub mod radarr_models;
pub mod servarr_data;
pub mod stateful_list;
@@ -29,6 +29,12 @@ pub enum Route {
Tautulli,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum Serdeable {
Radarr(RadarrSerdeable),
}
pub trait Scrollable {
fn scroll_down(&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")]
pub struct HorizontallyScrollableText {
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 {
fn from(text: String) -> HorizontallyScrollableText {
HorizontallyScrollableText::new(text)
}
}
impl From<&String> for HorizontallyScrollableText {
fn from(text: &String) -> HorizontallyScrollableText {
HorizontallyScrollableText::new(text.clone())
}
}
impl From<&str> for HorizontallyScrollableText {
fn from(text: &str) -> HorizontallyScrollableText {
HorizontallyScrollableText::new(text.to_owned())
@@ -109,14 +138,14 @@ impl From<&str> for HorizontallyScrollableText {
impl Display for HorizontallyScrollableText {
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)
} else {
let text_vec = self.text.chars().collect::<Vec<_>>();
write!(
f,
"{}",
text_vec[*self.offset.borrow()..]
text_vec[self.offset.load(Ordering::SeqCst)..]
.iter()
.cloned()
.collect::<String>()
@@ -138,7 +167,7 @@ impl HorizontallyScrollableText {
pub fn new(text: String) -> HorizontallyScrollableText {
HorizontallyScrollableText {
text,
offset: RefCell::new(0),
offset: AtomicUsize::new(0),
}
}
@@ -147,46 +176,44 @@ impl HorizontallyScrollableText {
}
pub fn scroll_left(&self) {
if *self.offset.borrow() < self.len() {
let new_offset = *self.offset.borrow() + 1;
*self.offset.borrow_mut() = new_offset;
if self.offset.load(Ordering::SeqCst) < self.len() {
self.offset.fetch_add(1, Ordering::SeqCst);
}
}
pub fn scroll_right(&self) {
if *self.offset.borrow() > 0 {
let new_offset = *self.offset.borrow() - 1;
*self.offset.borrow_mut() = new_offset;
if self.offset.load(Ordering::SeqCst) > 0 {
self.offset.fetch_sub(1, Ordering::SeqCst);
}
}
pub fn scroll_home(&self) {
*self.offset.borrow_mut() = self.len();
self.offset.store(self.len(), Ordering::SeqCst);
}
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) {
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();
} else {
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();
}
}
pub fn pop(&mut self) {
if *self.offset.borrow() < self.len() {
if self.offset.load(Ordering::SeqCst) < self.len() {
let (index, _) = self
.text
.chars()
.enumerate()
.nth(self.len() - *self.offset.borrow() - 1)
.nth(self.len() - self.offset.load(Ordering::SeqCst) - 1)
.unwrap();
self.text = self
.text
@@ -202,7 +229,7 @@ impl HorizontallyScrollableText {
if self.text.is_empty() {
self.text.push(character);
} else {
let index = self.len() - *self.offset.borrow();
let index = self.len() - self.offset.load(Ordering::SeqCst);
if index == self.len() {
self.text.push(character);
@@ -338,3 +365,16 @@ pub fn strip_non_search_characters(input: &str) -> String {
.replace_all(&input.to_lowercase(), "")
.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
View File
@@ -1,6 +1,7 @@
#[cfg(test)]
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 serde::de::value::Error as ValueError;
@@ -100,7 +101,22 @@ mod tests {
let test_text = "Test string";
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);
}
@@ -109,7 +125,10 @@ mod tests {
let test_text = "Test string";
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);
}
@@ -122,14 +141,14 @@ mod tests {
let horizontally_scrollable_text = HorizontallyScrollableText {
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");
let horizontally_scrollable_text = HorizontallyScrollableText {
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());
@@ -140,7 +159,10 @@ mod tests {
let test_text = "Test string";
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);
}
@@ -158,18 +180,24 @@ mod tests {
fn test_horizontally_scrollable_text_scroll_text_left() {
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 {
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();
assert_eq!(
*horizontally_scrollable_text.offset.borrow(),
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
horizontally_scrollable_text.text.len() - 1
);
}
@@ -180,37 +208,51 @@ mod tests {
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(), "");
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(), "");
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());
}
#[test]
fn test_horizontally_scrollable_text_scroll_text_right() {
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() {
horizontally_scrollable_text.scroll_right();
assert_eq!(
*horizontally_scrollable_text.offset.borrow(),
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
horizontally_scrollable_text.text.len() - i
);
}
horizontally_scrollable_text.scroll_right();
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
assert_eq!(
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
0
);
}
#[test]
@@ -220,7 +262,7 @@ mod tests {
horizontally_scrollable_text.scroll_home();
assert_eq!(
*horizontally_scrollable_text.offset.borrow(),
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
horizontally_scrollable_text.text.len()
);
}
@@ -231,19 +273,25 @@ mod tests {
horizontally_scrollable_text.scroll_home();
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 2);
assert_eq!(
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
2
);
}
#[test]
fn test_horizontally_scrollable_text_reset_offset() {
let horizontally_scrollable_text = HorizontallyScrollableText {
text: "Test string".to_owned(),
offset: RefCell::new(1),
offset: AtomicUsize::new(1),
};
horizontally_scrollable_text.reset_offset();
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
assert_eq!(
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
0
);
}
#[test]
@@ -254,23 +302,38 @@ mod tests {
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);
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);
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);
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);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
assert_eq!(
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
0
);
}
#[test]
@@ -278,11 +341,17 @@ mod tests {
let horizontally_scrollable_test = HorizontallyScrollableText::from("Test string");
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);
assert_eq!(*horizontally_scrollable_test.offset.borrow(), 0);
assert_eq!(
horizontally_scrollable_test.offset.load(Ordering::SeqCst),
0
);
}
#[test]
@@ -292,15 +361,24 @@ mod tests {
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);
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);
assert_eq!(*horizontally_scrollable_text.offset.borrow(), 0);
assert_eq!(
horizontally_scrollable_text.offset.load(Ordering::SeqCst),
0
);
}
#[test]
@@ -310,32 +388,47 @@ mod tests {
horizontally_scrollable_text.pop();
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.pop();
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.pop();
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.pop();
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.pop();
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]
@@ -344,17 +437,26 @@ mod tests {
horizontally_scrollable_text.pop();
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();
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();
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]
@@ -364,20 +466,29 @@ mod tests {
horizontally_scrollable_text.push('h');
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.push('l');
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.push('리');
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]
+199 -43
View File
@@ -1,18 +1,21 @@
use std::fmt::{Display, Formatter};
use chrono::{DateTime, Utc};
use clap::ValueEnum;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
use serde_json::{Number, Value};
use serde_json::{json, Number, Value};
use strum_macros::EnumIter;
use crate::models::HorizontallyScrollableText;
use crate::{models::HorizontallyScrollableText, serde_enum_from};
use super::Serdeable;
#[cfg(test)]
#[path = "radarr_models_tests.rs"]
mod radarr_models_tests;
#[derive(Default, Serialize, Debug)]
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AddMovieBody {
pub tmdb_id: i64,
@@ -25,7 +28,7 @@ pub struct AddMovieBody {
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")]
pub struct AddMovieSearchResult {
#[serde(deserialize_with = "super::from_i64")]
@@ -42,7 +45,7 @@ pub struct AddMovieSearchResult {
pub ratings: RatingsList,
}
#[derive(Default, Serialize, Debug, PartialEq, Eq)]
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AddOptions {
pub monitor: String,
@@ -54,12 +57,12 @@ pub struct AddRootFolderBody {
pub path: String,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlocklistResponse {
pub records: Vec<BlocklistItem>,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct BlocklistItem {
#[serde(deserialize_with = "super::from_i64")]
@@ -77,12 +80,12 @@ pub struct BlocklistItem {
pub movie: BlocklistItemMovie,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BlocklistItemMovie {
pub title: HorizontallyScrollableText,
}
#[derive(Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Derivative, Default, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Collection {
#[serde(deserialize_with = "super::from_i64")]
@@ -99,7 +102,7 @@ pub struct Collection {
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")]
pub struct CollectionMovie {
pub title: HorizontallyScrollableText,
@@ -120,7 +123,7 @@ pub struct CommandBody {
pub name: String,
}
#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Credit {
pub person_name: String,
@@ -131,7 +134,7 @@ pub struct Credit {
pub credit_type: CreditType,
}
#[derive(Deserialize, Default, PartialEq, Eq, Clone, Debug)]
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum CreditType {
#[default]
@@ -139,7 +142,15 @@ pub enum CreditType {
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")]
pub struct DiskSpace {
#[serde(deserialize_with = "super::from_i64")]
@@ -148,7 +159,7 @@ pub struct DiskSpace {
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")]
pub struct DownloadRecord {
pub title: String,
@@ -167,12 +178,51 @@ pub struct DownloadRecord {
pub download_client: String,
}
#[derive(Default, Deserialize, Debug)]
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
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)]
#[serde(rename_all = "camelCase")]
pub struct Indexer {
@@ -223,7 +273,7 @@ pub struct IndexerSettings {
pub whitelisted_hardcoded_subs: HorizontallyScrollableText,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct IndexerTestResult {
#[serde(deserialize_with = "super::from_i64")]
@@ -232,7 +282,7 @@ pub struct IndexerTestResult {
pub validation_failures: Vec<IndexerValidationFailure>,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct IndexerValidationFailure {
pub property_name: String,
@@ -240,12 +290,12 @@ pub struct IndexerValidationFailure {
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 name: String,
}
#[derive(Default, Deserialize, Clone, Debug, Eq, PartialEq)]
#[derive(Default, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Log {
pub time: DateTime<Utc>,
@@ -257,12 +307,12 @@ pub struct Log {
pub method: Option<String>,
}
#[derive(Default, Deserialize, Debug, Eq, PartialEq)]
#[derive(Default, Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct LogResponse {
pub records: Vec<Log>,
}
#[derive(Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Derivative, Debug, Clone, PartialEq, Eq)]
#[derivative(Default)]
#[serde(rename_all = "camelCase")]
pub struct MediaInfo {
@@ -286,7 +336,9 @@ pub struct MediaInfo {
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")]
pub enum MinimumAvailability {
#[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 {
#[default]
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")]
pub struct Movie {
#[serde(deserialize_with = "super::from_i64")]
@@ -380,7 +432,7 @@ pub struct Movie {
pub collection: Option<MovieCollection>,
}
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MovieCollection {
pub title: Option<String>,
@@ -393,7 +445,7 @@ pub struct MovieCommandBody {
pub movie_ids: Vec<i64>,
}
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MovieFile {
pub relative_path: String,
@@ -402,7 +454,7 @@ pub struct MovieFile {
pub media_info: Option<MediaInfo>,
}
#[derive(Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MovieHistoryItem {
pub source_title: HorizontallyScrollableText,
@@ -412,24 +464,33 @@ pub struct MovieHistoryItem {
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 name: String,
}
#[derive(Default, Deserialize, Debug)]
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct QualityProfile {
#[serde(deserialize_with = "super::from_i64")]
pub id: i64,
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 quality: Quality,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct QueueEvent {
pub trigger: String,
@@ -442,14 +503,14 @@ pub struct QueueEvent {
pub duration: Option<String>,
}
#[derive(Derivative, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Derivative, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derivative(Default)]
pub struct Rating {
#[derivative(Default(value = "Number::from(0)"))]
pub value: Number,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct RatingsList {
pub imdb: Option<Rating>,
@@ -457,7 +518,7 @@ pub struct RatingsList {
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(default)]
pub struct Release {
@@ -479,7 +540,7 @@ pub struct Release {
pub quality: QualityWrapper,
}
#[derive(Default, Serialize, Debug)]
#[derive(Default, Serialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ReleaseDownloadBody {
pub guid: String,
@@ -487,7 +548,7 @@ pub struct ReleaseDownloadBody {
pub movie_id: i64,
}
#[derive(Default, Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RootFolder {
#[serde(deserialize_with = "super::from_i64")]
@@ -499,25 +560,25 @@ pub struct RootFolder {
pub unmapped_folders: Option<Vec<UnmappedFolder>>,
}
#[derive(Deserialize, Debug)]
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SystemStatus {
pub version: String,
pub start_time: DateTime<Utc>,
}
#[derive(Default, Deserialize, Debug)]
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Tag {
#[serde(deserialize_with = "super::from_i64")]
pub id: i64,
pub label: String,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Task {
pub name: String,
pub task_name: String,
pub task_name: TaskName,
#[serde(deserialize_with = "super::from_i64")]
pub interval: i64,
pub last_execution: DateTime<Utc>,
@@ -525,13 +586,39 @@ pub struct Task {
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 name: String,
pub path: String,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Update {
pub version: String,
@@ -542,9 +629,78 @@ pub struct Update {
pub changes: UpdateChanges,
}
#[derive(Default, Deserialize, Debug, Clone, PartialEq, Eq)]
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct UpdateChanges {
pub new: 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>),
}
);
+337 -1
View File
@@ -1,8 +1,25 @@
#[cfg(test)]
mod tests {
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]
fn test_minimum_availability_display() {
@@ -70,4 +87,323 @@ mod tests {
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 {
root_folders: StatefulTable::default(),
disk_space_vec: Vec::new(),
version: String::default(),
version: String::new(),
start_time: DateTime::default(),
movies: StatefulTable::default(),
selected_block: BlockSelectionState::default(),
@@ -269,7 +269,7 @@ pub enum ActiveRadarrBlock {
FilterMovies,
FilterMoviesError,
Indexers,
IndexerSettingsPrompt,
AllIndexerSettingsPrompt,
IndexerSettingsAvailabilityDelayInput,
IndexerSettingsConfirmPrompt,
IndexerSettingsMaximumSizeInput,
@@ -466,7 +466,7 @@ pub static EDIT_INDEXER_NZB_SELECTION_BLOCKS: [ActiveRadarrBlock; 10] = [
ActiveRadarrBlock::EditIndexerConfirmPrompt,
];
pub static INDEXER_SETTINGS_BLOCKS: [ActiveRadarrBlock; 10] = [
ActiveRadarrBlock::IndexerSettingsPrompt,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput,
ActiveRadarrBlock::IndexerSettingsConfirmPrompt,
ActiveRadarrBlock::IndexerSettingsMaximumSizeInput,
@@ -423,7 +423,7 @@ mod tests {
#[test]
fn test_indexer_settings_blocks_contents() {
assert_eq!(INDEXER_SETTINGS_BLOCKS.len(), 10);
assert!(INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::IndexerSettingsPrompt));
assert!(INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::AllIndexerSettingsPrompt));
assert!(
INDEXER_SETTINGS_BLOCKS.contains(&ActiveRadarrBlock::IndexerSettingsAvailabilityDelayInput)
);