This commit is contained in:
2026-01-07 10:45:49 -07:00
parent 9b4eda6a9d
commit 3c1634d1e3
65 changed files with 2355 additions and 100 deletions
+88 -15
View File
@@ -1,3 +1,5 @@
use serde_json::Number;
use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES;
use crate::models::{
BlockSelectionState, Route, TabRoute, TabState,
@@ -8,9 +10,17 @@ use crate::models::{
use crate::network::lidarr_network::LidarrEvent;
use bimap::BiMap;
use chrono::{DateTime, Utc};
use strum::EnumIter;
use strum::{EnumIter};
use super::modals::EditArtistModal;
#[cfg(test)]
use strum::{Display, EnumString};
use {
strum::{Display, EnumString, IntoEnumIterator},
crate::models::lidarr_models::NewItemMonitorType,
crate::models::stateful_table::SortOption,
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::quality_profile_map,
crate::network::servarr_test_utils::diskspace,
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{download_record, metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map},
};
#[cfg(test)]
#[path = "lidarr_data_tests.rs"]
@@ -22,6 +32,7 @@ pub struct LidarrData<'a> {
pub delete_artist_files: bool,
pub disk_space_vec: Vec<DiskSpace>,
pub downloads: StatefulTable<DownloadRecord>,
pub edit_artist_modal: Option<EditArtistModal>,
pub main_tabs: TabState,
pub metadata_profile_map: BiMap<i64, String>,
pub prompt_confirm: bool,
@@ -39,6 +50,31 @@ impl LidarrData<'_> {
self.delete_artist_files = false;
self.add_import_list_exclusion = false;
}
pub fn tag_ids_to_display(&self, tag_ids: &[Number]) -> String {
tag_ids
.iter()
.filter_map(|id| {
let id = id.as_i64()?;
self.tags_map.get_by_left(&id).cloned()
})
.collect::<Vec<String>>()
.join(", ")
}
pub fn sorted_quality_profile_names(&self) -> Vec<String> {
let mut quality_profile_names: Vec<String> =
self.quality_profile_map.right_values().cloned().collect();
quality_profile_names.sort();
quality_profile_names
}
pub fn sorted_metadata_profile_names(&self) -> Vec<String> {
let mut metadata_profile_names: Vec<String> =
self.metadata_profile_map.right_values().cloned().collect();
metadata_profile_names.sort();
metadata_profile_names
}
}
impl<'a> Default for LidarrData<'a> {
@@ -49,6 +85,7 @@ impl<'a> Default for LidarrData<'a> {
delete_artist_files: false,
disk_space_vec: Vec::new(),
downloads: StatefulTable::default(),
edit_artist_modal: None,
metadata_profile_map: BiMap::new(),
prompt_confirm: false,
prompt_confirm_action: None,
@@ -71,11 +108,25 @@ impl<'a> Default for LidarrData<'a> {
#[cfg(test)]
impl LidarrData<'_> {
pub fn test_default_fully_populated() -> Self {
use crate::models::lidarr_models::{Artist, DownloadRecord};
use crate::models::servarr_models::{DiskSpace, RootFolder};
use crate::models::stateful_table::SortOption;
let mut edit_artist_modal = EditArtistModal {
monitored: Some(true),
path: "/nfs/music".into(),
tags: "alex".into(),
..EditArtistModal::default()
};
edit_artist_modal.monitor_list.set_items(NewItemMonitorType::iter().collect());
edit_artist_modal.quality_profile_list.set_items(vec![quality_profile().name]);
edit_artist_modal.metadata_profile_list.set_items(vec![metadata_profile().name]);
let mut lidarr_data = LidarrData::default();
let mut lidarr_data = LidarrData {
delete_artist_files: true,
disk_space_vec: vec![diskspace()],
quality_profile_map: quality_profile_map(),
metadata_profile_map: metadata_profile_map(),
edit_artist_modal: Some(edit_artist_modal),
tags_map: tags_map(),
..LidarrData::default()
};
lidarr_data.artists.set_items(vec![Artist::default()]);
lidarr_data.artists.sorting(vec![SortOption {
name: "Name",
@@ -83,19 +134,12 @@ impl LidarrData<'_> {
}]);
lidarr_data.artists.search = Some("artist search".into());
lidarr_data.artists.filter = Some("artist filter".into());
lidarr_data.quality_profile_map = BiMap::from_iter([(1i64, "Lossless".to_owned())]);
lidarr_data.metadata_profile_map = BiMap::from_iter([(1i64, "Standard".to_owned())]);
lidarr_data.tags_map = BiMap::from_iter([(1i64, "usenet".to_owned())]);
lidarr_data.disk_space_vec = vec![DiskSpace {
free_space: 50000000000,
total_space: 100000000000,
}];
lidarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
.set_items(vec![download_record()]);
lidarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
.set_items(vec![root_folder()]);
lidarr_data.version = "1.0.0".to_owned();
lidarr_data
@@ -112,6 +156,14 @@ pub enum ActiveLidarrBlock {
DeleteArtistConfirmPrompt,
DeleteArtistToggleDeleteFile,
DeleteArtistToggleAddListExclusion,
EditArtistPrompt,
EditArtistConfirmPrompt,
EditArtistPathInput,
EditArtistSelectMetadataProfile,
EditArtistSelectMonitorNewItems,
EditArtistSelectQualityProfile,
EditArtistTagsInput,
EditArtistToggleMonitored,
FilterArtists,
FilterArtistsError,
SearchArtists,
@@ -142,6 +194,27 @@ pub const DELETE_ARTIST_SELECTION_BLOCKS: &[&[ActiveLidarrBlock]] = &[
&[ActiveLidarrBlock::DeleteArtistConfirmPrompt],
];
pub static EDIT_ARTIST_BLOCKS: [ActiveLidarrBlock; 8] = [
ActiveLidarrBlock::EditArtistPrompt,
ActiveLidarrBlock::EditArtistConfirmPrompt,
ActiveLidarrBlock::EditArtistPathInput,
ActiveLidarrBlock::EditArtistSelectMetadataProfile,
ActiveLidarrBlock::EditArtistSelectMonitorNewItems,
ActiveLidarrBlock::EditArtistSelectQualityProfile,
ActiveLidarrBlock::EditArtistTagsInput,
ActiveLidarrBlock::EditArtistToggleMonitored,
];
pub const EDIT_ARTIST_SELECTION_BLOCKS: &[&[ActiveLidarrBlock]] = &[
&[ActiveLidarrBlock::EditArtistToggleMonitored],
&[ActiveLidarrBlock::EditArtistSelectMonitorNewItems],
&[ActiveLidarrBlock::EditArtistSelectQualityProfile],
&[ActiveLidarrBlock::EditArtistSelectMetadataProfile],
&[ActiveLidarrBlock::EditArtistPathInput],
&[ActiveLidarrBlock::EditArtistTagsInput],
&[ActiveLidarrBlock::EditArtistConfirmPrompt],
];
impl From<ActiveLidarrBlock> for Route {
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
Route::Lidarr(active_lidarr_block, None)
@@ -1,15 +1,15 @@
#[cfg(test)]
mod tests {
use bimap::BiMap;
use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES;
use crate::models::servarr_data::lidarr::lidarr_data::{
DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS,
};
use crate::models::servarr_data::lidarr::lidarr_data::{DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS};
use crate::models::{
BlockSelectionState, Route,
servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS, LidarrData},
};
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
#[test]
fn test_from_active_lidarr_block_to_route() {
@@ -41,6 +41,50 @@ mod tests {
assert!(!lidarr_data.add_import_list_exclusion);
}
#[test]
fn test_tag_ids_to_display() {
let mut tags_map = BiMap::new();
tags_map.insert(3, "test 3".to_owned());
tags_map.insert(2, "test 2".to_owned());
tags_map.insert(1, "test 1".to_owned());
let lidarr_data = LidarrData {
tags_map,
..LidarrData::default()
};
assert_str_eq!(lidarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]), "test 1, test 2");
}
#[test]
fn test_sorted_quality_profile_names() {
let mut quality_profile_map = BiMap::new();
quality_profile_map.insert(3, "test 3".to_owned());
quality_profile_map.insert(2, "test 2".to_owned());
quality_profile_map.insert(1, "test 1".to_owned());
let lidarr_data = LidarrData {
quality_profile_map,
..LidarrData::default()
};
let expected_quality_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()];
assert_iter_eq!(lidarr_data.sorted_quality_profile_names(), expected_quality_profile_vec);
}
#[test]
fn test_sorted_metadata_profile_names() {
let mut metadata_profile_map = BiMap::new();
metadata_profile_map.insert(3, "test 3".to_owned());
metadata_profile_map.insert(2, "test 2".to_owned());
metadata_profile_map.insert(1, "test 1".to_owned());
let lidarr_data = LidarrData {
metadata_profile_map,
..LidarrData::default()
};
let expected_metadata_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()];
assert_iter_eq!(lidarr_data.sorted_metadata_profile_names(), expected_metadata_profile_vec);
}
#[test]
fn test_lidarr_data_default() {
let lidarr_data = LidarrData::default();
@@ -50,6 +94,7 @@ mod tests {
assert!(!lidarr_data.delete_artist_files);
assert_is_empty!(lidarr_data.disk_space_vec);
assert_is_empty!(lidarr_data.downloads);
assert_none!(lidarr_data.edit_artist_modal);
assert_is_empty!(lidarr_data.metadata_profile_map);
assert!(!lidarr_data.prompt_confirm);
assert_none!(lidarr_data.prompt_confirm_action);
@@ -113,4 +158,31 @@ mod tests {
);
assert_none!(delete_artist_block_iter.next());
}
#[test]
fn test_edit_artist_blocks() {
assert_eq!(EDIT_ARTIST_BLOCKS.len(), 8);
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistPrompt));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistConfirmPrompt));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistPathInput));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistSelectMetadataProfile));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistSelectMonitorNewItems));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistSelectQualityProfile));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistTagsInput));
assert!(EDIT_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::EditArtistToggleMonitored));
}
#[test]
fn test_edit_artist_selection_blocks_ordering() {
let mut edit_artist_block_iter = EDIT_ARTIST_SELECTION_BLOCKS.iter();
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistToggleMonitored]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectMonitorNewItems]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectQualityProfile]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectMetadataProfile]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistPathInput]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistTagsInput]);
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistConfirmPrompt]);
assert_none!(edit_artist_block_iter.next());
}
}
+1
View File
@@ -1 +1,2 @@
pub mod lidarr_data;
pub mod modals;
+78
View File
@@ -0,0 +1,78 @@
use strum::IntoEnumIterator;
use super::lidarr_data::LidarrData;
use crate::models::{
HorizontallyScrollableText, lidarr_models::NewItemMonitorType, stateful_list::StatefulList,
};
#[cfg(test)]
#[path = "modals_tests.rs"]
mod modals_tests;
#[derive(Default)]
#[cfg_attr(test, derive(Debug))]
pub struct EditArtistModal {
pub monitor_list: StatefulList<NewItemMonitorType>,
pub quality_profile_list: StatefulList<String>,
pub metadata_profile_list: StatefulList<String>,
pub monitored: Option<bool>,
pub path: HorizontallyScrollableText,
pub tags: HorizontallyScrollableText,
}
impl From<&LidarrData<'_>> for EditArtistModal {
fn from(lidarr_data: &LidarrData<'_>) -> EditArtistModal {
let mut edit_artist_modal = EditArtistModal::default();
let artist = lidarr_data.artists.current_selection();
edit_artist_modal
.monitor_list
.set_items(Vec::from_iter(NewItemMonitorType::iter()));
edit_artist_modal.path = artist.path.clone().into();
edit_artist_modal.tags = lidarr_data.tag_ids_to_display(&artist.tags).into();
edit_artist_modal.monitored = Some(artist.monitored);
let monitor_index = edit_artist_modal
.monitor_list
.items
.iter()
.position(|m| *m == artist.monitor_new_items);
edit_artist_modal.monitor_list.state.select(monitor_index);
edit_artist_modal
.quality_profile_list
.set_items(lidarr_data.sorted_quality_profile_names());
let quality_profile_name = lidarr_data
.quality_profile_map
.get_by_left(&artist.quality_profile_id)
.unwrap();
let quality_profile_index = edit_artist_modal
.quality_profile_list
.items
.iter()
.position(|profile| profile == quality_profile_name);
edit_artist_modal
.quality_profile_list
.state
.select(quality_profile_index);
edit_artist_modal
.metadata_profile_list
.set_items(lidarr_data.sorted_metadata_profile_names());
let metadata_profile_name = lidarr_data
.metadata_profile_map
.get_by_left(&artist.metadata_profile_id)
.unwrap();
let metadata_profile_index = edit_artist_modal
.metadata_profile_list
.items
.iter()
.position(|profile| profile == metadata_profile_name);
edit_artist_modal
.metadata_profile_list
.state
.select(metadata_profile_index);
edit_artist_modal
}
}
@@ -0,0 +1,48 @@
#[cfg(test)]
mod tests {
use bimap::BiMap;
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::models::lidarr_models::{Artist, NewItemMonitorType};
use crate::models::servarr_data::lidarr::lidarr_data::LidarrData;
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
#[test]
fn test_edit_artist_modal_from_lidarr_data() {
let mut lidarr_data = LidarrData {
quality_profile_map: BiMap::from_iter([(1i64, "HD - 1080p".to_owned()), (2i64, "Any".to_owned())]),
metadata_profile_map: BiMap::from_iter([(1i64, "Standard".to_owned()), (2i64, "None".to_owned())]),
tags_map: BiMap::from_iter([(1i64, "usenet".to_owned())]),
..LidarrData::default()
};
let artist = Artist {
id: 1,
monitored: true,
monitor_new_items: NewItemMonitorType::All,
quality_profile_id: 1,
metadata_profile_id: 1,
path: "/nfs/music/test_artist".to_owned(),
tags: vec![serde_json::Number::from(1)],
..Artist::default()
};
lidarr_data.artists.set_items(vec![artist]);
let edit_artist_modal = EditArtistModal::from(&lidarr_data);
assert_eq!(edit_artist_modal.monitored, Some(true));
assert_eq!(
*edit_artist_modal.monitor_list.current_selection(),
NewItemMonitorType::All
);
assert_str_eq!(
edit_artist_modal.quality_profile_list.current_selection(),
"HD - 1080p"
);
assert_str_eq!(
edit_artist_modal.metadata_profile_list.current_selection(),
"Standard"
);
assert_str_eq!(edit_artist_modal.path.text, "/nfs/music/test_artist");
assert_str_eq!(edit_artist_modal.tags.text, "usenet");
}
}