diff --git a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs index 9eabb627..835145c9 100644 --- a/src/handlers/radarr_handlers/radarr_handler_test_utils.rs +++ b/src/handlers/radarr_handlers/radarr_handler_test_utils.rs @@ -5,7 +5,7 @@ pub(in crate::handlers::radarr_handlers) mod utils { use crate::models::radarr_models::{ AddMovieBody, AddMovieOptions, AddMovieSearchResult, Collection, CollectionMovie, DownloadRecord, DownloadStatus, IndexerSettings, MediaInfo, MinimumAvailability, Movie, - MovieCollection, MovieFile, MovieMonitor, RadarrRelease, Rating, RatingsList, + MovieCollection, MovieFile, MovieMonitor, MovieStatus, RadarrRelease, Rating, RatingsList, }; use crate::models::servarr_models::{ Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder, @@ -329,7 +329,7 @@ pub(in crate::handlers::radarr_handlers) mod utils { title: "Test".to_owned().into(), original_language: language(), size_on_disk: 3543348019, - status: "Downloaded".to_owned(), + status: MovieStatus::Released, overview: "Blah blah blah".to_owned(), path: "/nfs/movies".to_owned(), studio: Some("21st Century Alex".to_owned()), @@ -395,7 +395,7 @@ pub(in crate::handlers::radarr_handlers) mod utils { tmdb_id: 4321, title: HorizontallyScrollableText::from("Test Search Result"), original_language: language(), - status: "released".to_owned(), + status: MovieStatus::Released, overview: "New movie blah blah blah".to_owned(), genres: genres(), year: 2023, diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index a5792855..02a5b831 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -42,7 +42,8 @@ pub struct AddMovieSearchResult { pub tmdb_id: i64, pub title: HorizontallyScrollableText, pub original_language: Language, - pub status: String, + #[serde(deserialize_with = "super::from_json_or_default")] + pub status: MovieStatus, pub overview: String, pub genres: Vec, #[serde(deserialize_with = "super::from_i64")] @@ -333,7 +334,8 @@ pub struct Movie { pub original_language: Language, #[serde(deserialize_with = "super::from_i64")] pub size_on_disk: i64, - pub status: String, + #[serde(deserialize_with = "super::from_json_or_default")] + pub status: MovieStatus, pub overview: String, pub path: String, pub studio: Option, @@ -357,6 +359,32 @@ pub struct Movie { pub collection: Option, } +#[derive( + Serialize, + Deserialize, + Default, + PartialEq, + Eq, + Clone, + Copy, + Debug, + EnumIter, + Display, + EnumDisplayStyle, +)] +#[serde(rename_all = "camelCase")] +#[strum(serialize_all = "camelCase")] +pub enum MovieStatus { + #[default] + #[display_style(name = "TBA")] + Tba, + Announced, + #[display_style(name = "In Cinemas")] + InCinemas, + Released, + Deleted, +} + #[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct MovieCollection { diff --git a/src/models/radarr_models_tests.rs b/src/models/radarr_models_tests.rs index 7b9e0d6a..93e6dbf5 100644 --- a/src/models/radarr_models_tests.rs +++ b/src/models/radarr_models_tests.rs @@ -12,7 +12,7 @@ mod tests { radarr_models::{ AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, CreditType, DiskSpace, DownloadRecord, DownloadStatus, DownloadsResponse, Indexer, IndexerSettings, - IndexerTestResult, MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, + IndexerTestResult, MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, MovieStatus, QualityProfile, RadarrRelease, RadarrSerdeable, RadarrTask, RadarrTaskName, Tag, Update, }, servarr_models::{ @@ -20,7 +20,7 @@ mod tests { }, }; use crate::network::radarr_network::radarr_network_test_utils::test_utils::{ - MOVIE_JSON, collection, crew_credit, download_record, task, + MOVIE_JSON, add_movie_search_result, collection, crew_credit, download_record, task, }; #[test] @@ -50,6 +50,24 @@ mod tests { assert_str_eq!(MinimumAvailability::Released.to_display_str(), "Released"); } + #[test] + fn test_movie_status_display() { + assert_str_eq!(MovieStatus::Tba.to_string(), "tba"); + assert_str_eq!(MovieStatus::Announced.to_string(), "announced"); + assert_str_eq!(MovieStatus::InCinemas.to_string(), "inCinemas"); + assert_str_eq!(MovieStatus::Released.to_string(), "released"); + assert_str_eq!(MovieStatus::Deleted.to_string(), "deleted"); + } + + #[test] + fn test_movie_status_to_display_str() { + assert_str_eq!(MovieStatus::Tba.to_display_str(), "TBA"); + assert_str_eq!(MovieStatus::Announced.to_display_str(), "Announced"); + assert_str_eq!(MovieStatus::InCinemas.to_display_str(), "In Cinemas"); + assert_str_eq!(MovieStatus::Released.to_display_str(), "Released"); + assert_str_eq!(MovieStatus::Deleted.to_display_str(), "Deleted"); + } + #[test] fn test_monitor_display() { assert_str_eq!(MovieMonitor::MovieOnly.to_string(), "movieOnly"); @@ -681,6 +699,49 @@ mod tests { ); } + #[test] + fn test_movie_status_deserialization_falls_back_to_default() { + let expected = Movie { + status: MovieStatus::default(), + ..serde_json::from_str(MOVIE_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(MOVIE_JSON, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(MOVIE_JSON, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_add_movie_search_result_status_deserialization_falls_back_to_default() { + let add_movie_search_result_json = serde_json::to_string(&add_movie_search_result()).unwrap(); + let expected = AddMovieSearchResult { + status: MovieStatus::default(), + ..add_movie_search_result() + }; + + assert_eq!( + deserialize_with_field::( + &add_movie_search_result_json, + "status", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + &add_movie_search_result_json, + "status", + json!("notAThing") + ), + expected + ); + } + #[test] fn test_collection_minimum_availability_deserialization_falls_back_to_default() { let collection_json = serde_json::to_string(&collection()).unwrap(); diff --git a/src/network/radarr_network/blocklist/radarr_blocklist_network_tests.rs b/src/network/radarr_network/blocklist/radarr_blocklist_network_tests.rs index d74b5d05..d514b254 100644 --- a/src/network/radarr_network/blocklist/radarr_blocklist_network_tests.rs +++ b/src/network/radarr_network/blocklist/radarr_blocklist_network_tests.rs @@ -91,7 +91,7 @@ mod tests { "tmdbId": 1234, "originalLanguage": {"id": 1, "name": "English"}, "sizeOnDisk": 3543348019i64, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", @@ -127,7 +127,7 @@ mod tests { "tmdbId": 1234, "originalLanguage": {"id": 1, "name": "English"}, "sizeOnDisk": 3543348019i64, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", @@ -230,7 +230,7 @@ mod tests { "tmdbId": 1234, "originalLanguage": {"id": 1, "name": "English"}, "sizeOnDisk": 3543348019i64, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", @@ -266,7 +266,7 @@ mod tests { "tmdbId": 1234, "originalLanguage": {"id": 1, "name": "English"}, "sizeOnDisk": 3543348019i64, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", diff --git a/src/network/radarr_network/library/radarr_library_network_tests.rs b/src/network/radarr_network/library/radarr_library_network_tests.rs index ca4f44d4..78ba924e 100644 --- a/src/network/radarr_network/library/radarr_library_network_tests.rs +++ b/src/network/radarr_network/library/radarr_library_network_tests.rs @@ -701,7 +701,7 @@ mod tests { "name": "English" }, "sizeOnDisk": 0, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", diff --git a/src/network/radarr_network/radarr_network_test_utils.rs b/src/network/radarr_network/radarr_network_test_utils.rs index 80a719db..c64feb06 100644 --- a/src/network/radarr_network/radarr_network_test_utils.rs +++ b/src/network/radarr_network/radarr_network_test_utils.rs @@ -3,9 +3,9 @@ pub mod test_utils { use crate::models::radarr_models::{ AddMovieSearchResult, BlocklistItem, BlocklistItemMovie, Collection, CollectionMovie, Credit, CreditType, DownloadRecord, DownloadStatus, DownloadsResponse, IndexerSettings, MediaInfo, - MinimumAvailability, Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrHistoryData, - RadarrHistoryEventType, RadarrHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName, Rating, - RatingsList, + MinimumAvailability, Movie, MovieCollection, MovieFile, MovieHistoryItem, MovieStatus, + RadarrHistoryData, RadarrHistoryEventType, RadarrHistoryItem, RadarrRelease, RadarrTask, + RadarrTaskName, Rating, RatingsList, }; use crate::models::servarr_models::{ Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder, @@ -25,7 +25,7 @@ pub mod test_utils { "name": "English" }, "sizeOnDisk": 3543348019, - "status": "Downloaded", + "status": "released", "overview": "Blah blah blah", "path": "/nfs/movies", "studio": "21st Century Alex", @@ -234,7 +234,7 @@ pub mod test_utils { title: "Test".to_owned().into(), original_language: language(), size_on_disk: 3543348019, - status: "Downloaded".to_owned(), + status: MovieStatus::Released, overview: "Blah blah blah".to_owned(), path: "/nfs/movies".to_owned(), studio: Some("21st Century Alex".to_owned()), @@ -300,7 +300,7 @@ pub mod test_utils { tmdb_id: 1234, title: HorizontallyScrollableText::from("Test"), original_language: language(), - status: "released".to_owned(), + status: MovieStatus::Released, overview: "New movie blah blah blah".to_owned(), genres: genres(), year: 2023, diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 60bac887..fa2f5903 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -13,7 +13,7 @@ use serde_json::Number; use crate::app::App; use crate::models::Route; -use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease}; +use crate::models::radarr_models::{Credit, MovieHistoryItem, MovieStatus, RadarrRelease}; use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; use crate::ui::styles::ManagarrStyle; @@ -179,13 +179,7 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { match app.data.radarr_data.movie_details_modal.as_ref() { Some(movie_details_modal) if !app.is_loading => { let is_monitored = app.data.radarr_data.movies.current_selection().monitored; - let status = app - .data - .radarr_data - .movies - .current_selection() - .status - .clone(); + let status = app.data.radarr_data.movies.current_selection().status; let movie_details = &movie_details_modal.movie_details; let download_status = movie_details .items @@ -201,7 +195,7 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { .map(|line| { let split = line.split(':').collect::>(); let title = format!("{}:", split[0]); - let style = style_from_download_status(download_status, is_monitored, status.clone()); + let style = style_from_download_status(download_status, is_monitored, status); Line::from(vec![ Span::styled(title, style.bold()), @@ -531,13 +525,17 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) { } } -fn style_from_download_status(download_status: &str, is_monitored: bool, status: String) -> Style { +fn style_from_download_status( + download_status: &str, + is_monitored: bool, + status: MovieStatus, +) -> Style { match download_status { "Downloaded" => downloaded_style(), "Awaiting Import" => awaiting_import_style(), "Downloading" => downloading_style(), _ if !is_monitored && download_status == "Missing" => unmonitored_missing_style(), - _ if status != "released" && download_status == "Missing" => unreleased_style(), + _ if status != MovieStatus::Released && download_status == "Missing" => unreleased_style(), "Missing" => missing_style(), _ => downloaded_style(), } diff --git a/src/ui/radarr_ui/library/movie_details_ui_tests.rs b/src/ui/radarr_ui/library/movie_details_ui_tests.rs index 551297dc..cc5bf18e 100644 --- a/src/ui/radarr_ui/library/movie_details_ui_tests.rs +++ b/src/ui/radarr_ui/library/movie_details_ui_tests.rs @@ -6,6 +6,7 @@ mod tests { use strum::IntoEnumIterator; use crate::app::App; + use crate::models::radarr_models::MovieStatus; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; use crate::ui::DrawUi; use crate::ui::radarr_ui::library::movie_details_ui::{ @@ -13,7 +14,7 @@ mod tests { }; use crate::ui::styles::{ awaiting_import_style, downloaded_style, downloading_style, missing_style, - unmonitored_missing_style, + unmonitored_missing_style, unreleased_style, }; use crate::ui::ui_test_utils::test_utils::{TerminalSize, render_to_string_with_app}; @@ -29,21 +30,26 @@ mod tests { } #[rstest] - #[case("Downloading", true, "", downloading_style())] - #[case("Downloaded", true, "", downloaded_style())] - #[case("Awaiting Import", true, "", awaiting_import_style())] - #[case("Missing", false, "", unmonitored_missing_style())] - #[case("Missing", false, "", unmonitored_missing_style())] - #[case("Missing", true, "released", missing_style())] - #[case("", true, "", downloaded_style())] + #[case("Downloading", true, MovieStatus::Released, downloading_style())] + #[case("Downloaded", true, MovieStatus::Released, downloaded_style())] + #[case( + "Awaiting Import", + true, + MovieStatus::Released, + awaiting_import_style() + )] + #[case("Missing", false, MovieStatus::InCinemas, unmonitored_missing_style())] + #[case("Missing", true, MovieStatus::InCinemas, unreleased_style())] + #[case("Missing", true, MovieStatus::Released, missing_style())] + #[case("", true, MovieStatus::Released, downloaded_style())] fn test_style_from_download_status( #[case] download_status: &str, #[case] is_monitored: bool, - #[case] movie_status: &str, + #[case] movie_status: MovieStatus, #[case] expected_style: Style, ) { assert_eq!( - style_from_download_status(download_status, is_monitored, movie_status.to_owned()), + style_from_download_status(download_status, is_monitored, movie_status), expected_style ); } diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index a841f003..a50fc898 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -1,7 +1,7 @@ use crate::app::App; use crate::logos::RADARR_LOGO; use crate::models::Route; -use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie}; +use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie, MovieStatus}; use crate::models::servarr_data::radarr::radarr_data::RadarrData; use crate::models::servarr_models::{DiskSpace, RootFolder}; use crate::ui::DrawUi; @@ -243,7 +243,7 @@ fn decorate_with_row_style<'a>( return row.unmonitored_missing(); } - if movie.status != "released" { + if movie.status != MovieStatus::Released { return row.unreleased(); } diff --git a/src/ui/radarr_ui/radarr_ui_tests.rs b/src/ui/radarr_ui/radarr_ui_tests.rs index 680269cc..17b2dc75 100644 --- a/src/ui/radarr_ui/radarr_ui_tests.rs +++ b/src/ui/radarr_ui/radarr_ui_tests.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie}; + use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie, MovieStatus}; use pretty_assertions::assert_eq; use ratatui::widgets::{Cell, Row}; use rstest::rstest; @@ -54,26 +54,32 @@ mod tests { false, Some(DownloadStatus::Downloading), false, - "", + MovieStatus::Released, RowStyle::Downloading )] #[case( false, Some(DownloadStatus::Completed), false, - "", + MovieStatus::Released, RowStyle::AwaitingImport )] - #[case(false, None, false, "", RowStyle::UnmonitoredMissing)] - #[case(false, None, true, "", RowStyle::Unreleased)] - #[case(false, None, true, "released", RowStyle::Missing)] - #[case(true, None, false, "", RowStyle::Unmonitored)] - #[case(true, None, true, "", RowStyle::Downloaded)] + #[case( + false, + None, + false, + MovieStatus::Released, + RowStyle::UnmonitoredMissing + )] + #[case(false, None, true, MovieStatus::InCinemas, RowStyle::Unreleased)] + #[case(false, None, true, MovieStatus::Released, RowStyle::Missing)] + #[case(true, None, false, MovieStatus::Released, RowStyle::Unmonitored)] + #[case(true, None, true, MovieStatus::Released, RowStyle::Downloaded)] fn test_decorate_with_row_style( #[case] has_file: bool, #[case] download_status: Option, #[case] is_monitored: bool, - #[case] movie_status: String, + #[case] movie_status: MovieStatus, #[case] expected_style: RowStyle, ) { let downloads_vec = if let Some(download_status) = download_status {