diff --git a/src/models/lidarr_models.rs b/src/models/lidarr_models.rs index 333d51d9..74fe95b1 100644 --- a/src/models/lidarr_models.rs +++ b/src/models/lidarr_models.rs @@ -404,6 +404,7 @@ pub struct LidarrHistoryItem { #[serde(default)] pub quality: QualityWrapper, pub date: DateTime, + #[serde(deserialize_with = "super::from_json_or_default")] pub event_type: LidarrHistoryEventType, #[serde(default)] pub data: LidarrHistoryData, diff --git a/src/models/lidarr_models_tests.rs b/src/models/lidarr_models_tests.rs index f3fd6ad4..618d6f5f 100644 --- a/src/models/lidarr_models_tests.rs +++ b/src/models/lidarr_models_tests.rs @@ -2,7 +2,7 @@ mod tests { use chrono::Utc; use pretty_assertions::{assert_eq, assert_str_eq}; - use serde_json::json; + use serde_json::{Value, json}; use crate::models::lidarr_models::{ AddArtistSearchResult, Album, AudioTags, BlocklistItem, BlocklistResponse, DownloadRecord, @@ -773,6 +773,126 @@ mod tests { ); } + #[test] + fn test_lidarr_history_event_type_deserialization() { + assert_eq!( + deserialize_history_event_type(json!("unknown")), + LidarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!("grabbed")), + LidarrHistoryEventType::Grabbed + ); + assert_eq!( + deserialize_history_event_type(json!("artistFolderImported")), + LidarrHistoryEventType::ArtistFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("albumImportIncomplete")), + LidarrHistoryEventType::AlbumImportIncomplete + ); + assert_eq!( + deserialize_history_event_type(json!("downloadIgnored")), + LidarrHistoryEventType::DownloadIgnored + ); + assert_eq!( + deserialize_history_event_type(json!("downloadImported")), + LidarrHistoryEventType::DownloadImported + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFailed")), + LidarrHistoryEventType::DownloadFailed + ); + assert_eq!( + deserialize_history_event_type(json!("trackFileDeleted")), + LidarrHistoryEventType::TrackFileDeleted + ); + assert_eq!( + deserialize_history_event_type(json!("trackFileImported")), + LidarrHistoryEventType::TrackFileImported + ); + assert_eq!( + deserialize_history_event_type(json!("trackFileRenamed")), + LidarrHistoryEventType::TrackFileRenamed + ); + assert_eq!( + deserialize_history_event_type(json!("trackFileRetagged")), + LidarrHistoryEventType::TrackFileRetagged + ); + } + + #[test] + fn test_lidarr_history_event_type_deserialization_falls_back_to_unknown() { + assert_eq!( + deserialize_history_event_type(json!("albumImportPartiallyComplete")), + LidarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(2)), + LidarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(11)), + LidarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(null)), + LidarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!({ "id": 2 })), + LidarrHistoryEventType::Unknown + ); + } + + #[test] + fn test_lidarr_history_event_type_serialization() { + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::Unknown), + "unknown" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::Grabbed), + "grabbed" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::ArtistFolderImported), + "artistFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::AlbumImportIncomplete), + "albumImportIncomplete" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::DownloadIgnored), + "downloadIgnored" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::DownloadImported), + "downloadImported" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::DownloadFailed), + "downloadFailed" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::TrackFileDeleted), + "trackFileDeleted" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::TrackFileImported), + "trackFileImported" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::TrackFileRenamed), + "trackFileRenamed" + ); + assert_str_eq!( + serialize_history_event_type(LidarrHistoryEventType::TrackFileRetagged), + "trackFileRetagged" + ); + } + #[test] fn test_add_artist_search_result_deserialization() { let search_result_json = json!({ @@ -825,4 +945,32 @@ mod tests { assert!(search_result.genres.is_empty()); assert_none!(&search_result.ratings); } + + fn deserialize_history_event_type(event_type: Value) -> LidarrHistoryEventType { + let history_item_json = json!({ + "id": 1, + "sourceTitle": "Test Source Title", + "albumId": 1, + "artistId": 1, + "trackId": 1, + "date": "2024-01-01T00:00:00Z", + "eventType": event_type + }); + + serde_json::from_value::(history_item_json) + .unwrap() + .event_type + } + + fn serialize_history_event_type(event_type: LidarrHistoryEventType) -> String { + let history_item = LidarrHistoryItem { + event_type, + ..LidarrHistoryItem::default() + }; + + serde_json::to_value(history_item).unwrap()["eventType"] + .as_str() + .unwrap() + .to_owned() + } } diff --git a/src/models/mod.rs b/src/models/mod.rs index 867da8b1..870be97d 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -11,7 +11,7 @@ use radarr_models::RadarrSerdeable; use readarr_models::ReadarrSerdeable; use regex::Regex; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; -use serde_json::Number; +use serde_json::{Number, Value}; use servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use sonarr_models::SonarrSerdeable; @@ -442,6 +442,15 @@ where ))) } +pub fn from_json_or_default<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: Deserialize<'de> + Default, +{ + let value: Value = Deserialize::deserialize(deserializer)?; + Ok(T::deserialize(value).unwrap_or_default()) +} + pub fn strip_non_search_characters(input: &str) -> String { Regex::new(r"[^a-zA-Z0-9.,/'\-:\s]") .unwrap() diff --git a/src/models/model_tests.rs b/src/models/model_tests.rs index 1b8dc3e7..3f572369 100644 --- a/src/models/model_tests.rs +++ b/src/models/model_tests.rs @@ -11,12 +11,13 @@ mod tests { use crate::models::{ BlockSelectionState, HorizontallyScrollableText, Scrollable, ScrollableText, TabRoute, TabState, }; - use crate::models::{from_i64, strip_non_search_characters}; + use crate::models::{from_i64, from_json_or_default, strip_non_search_characters}; use pretty_assertions::{assert_eq, assert_str_eq}; use serde::de::IntoDeserializer; use serde::de::value::Error as ValueError; use serde::de::value::F64Deserializer; use serde::de::value::I64Deserializer; + use serde::de::value::StrDeserializer; use serde_json::to_string; const BLOCKS: &[&[i32]] = &[&[11, 12], &[21, 22], &[31, 32]]; @@ -803,6 +804,20 @@ mod tests { assert_ok_eq_x!(from_f64(deserializer), 1.0); } + #[test] + fn test_from_json_or_default() { + let deserializer: I64Deserializer = 1i64.into_deserializer(); + + assert_ok_eq_x!(from_json_or_default::<_, i64>(deserializer), 1); + } + + #[test] + fn test_from_json_or_default_falls_back_to_default() { + let deserializer: StrDeserializer<'_, ValueError> = "unexpected".into_deserializer(); + + assert_ok_eq_x!(from_json_or_default::<_, i64>(deserializer), 0); + } + #[test] fn test_horizontally_scrollable_serialize() { let text = HorizontallyScrollableText::from("Test"); diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index 8b59fd3d..c71fe71f 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -459,6 +459,7 @@ pub struct RadarrHistoryItem { pub quality: QualityWrapper, pub languages: Vec, pub date: DateTime, + #[serde(deserialize_with = "super::from_json_or_default")] pub event_type: RadarrHistoryEventType, #[serde(default)] pub data: RadarrHistoryData, diff --git a/src/models/radarr_models_tests.rs b/src/models/radarr_models_tests.rs index b9ed2485..a114a17d 100644 --- a/src/models/radarr_models_tests.rs +++ b/src/models/radarr_models_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; - use serde_json::json; + use serde_json::{Value, json}; use crate::models::radarr_models::{ RadarrHistoryEventType, RadarrHistoryItem, RadarrHistoryWrapper, @@ -126,6 +126,102 @@ mod tests { ); } + #[test] + fn test_radarr_history_event_type_deserialization() { + assert_eq!( + deserialize_history_event_type(json!("unknown")), + RadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!("grabbed")), + RadarrHistoryEventType::Grabbed + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFolderImported")), + RadarrHistoryEventType::DownloadFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFailed")), + RadarrHistoryEventType::DownloadFailed + ); + assert_eq!( + deserialize_history_event_type(json!("movieFileDeleted")), + RadarrHistoryEventType::MovieFileDeleted + ); + assert_eq!( + deserialize_history_event_type(json!("movieFolderImported")), + RadarrHistoryEventType::MovieFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("movieFileRenamed")), + RadarrHistoryEventType::MovieFileRenamed + ); + assert_eq!( + deserialize_history_event_type(json!("downloadIgnored")), + RadarrHistoryEventType::DownloadIgnored + ); + } + + #[test] + fn test_radarr_history_event_type_deserialization_falls_back_to_unknown() { + assert_eq!( + deserialize_history_event_type(json!("movieFileImportFailed")), + RadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(2)), + RadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(11)), + RadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(null)), + RadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!({ "id": 2 })), + RadarrHistoryEventType::Unknown + ); + } + + #[test] + fn test_radarr_history_event_type_serialization() { + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::Unknown), + "unknown" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::Grabbed), + "grabbed" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::DownloadFolderImported), + "downloadFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::DownloadFailed), + "downloadFailed" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::MovieFileDeleted), + "movieFileDeleted" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::MovieFolderImported), + "movieFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::MovieFileRenamed), + "movieFileRenamed" + ); + assert_str_eq!( + serialize_history_event_type(RadarrHistoryEventType::DownloadIgnored), + "downloadIgnored" + ); + } + #[test] fn test_download_record_default_indexer_value() { let json = r#"{ @@ -517,4 +613,32 @@ mod tests { RadarrSerdeable::IndexerTestResults(indexer_test_results) ); } + + fn deserialize_history_event_type(event_type: Value) -> RadarrHistoryEventType { + let history_item_json = json!({ + "id": 1, + "sourceTitle": "Test Source Title", + "movieId": 1, + "quality": { "quality": { "name": "HD - 1080p" } }, + "languages": [], + "date": "2024-01-01T00:00:00Z", + "eventType": event_type + }); + + serde_json::from_value::(history_item_json) + .unwrap() + .event_type + } + + fn serialize_history_event_type(event_type: RadarrHistoryEventType) -> String { + let history_item = RadarrHistoryItem { + event_type, + ..RadarrHistoryItem::default() + }; + + serde_json::to_value(history_item).unwrap()["eventType"] + .as_str() + .unwrap() + .to_owned() + } } diff --git a/src/models/readarr_models.rs b/src/models/readarr_models.rs index bb2f10e6..17dc3257 100644 --- a/src/models/readarr_models.rs +++ b/src/models/readarr_models.rs @@ -413,6 +413,7 @@ pub struct ReadarrHistoryItem { #[serde(default)] pub quality: QualityWrapper, pub date: DateTime, + #[serde(deserialize_with = "super::from_json_or_default")] pub event_type: ReadarrHistoryEventType, #[serde(default)] pub data: ReadarrHistoryData, diff --git a/src/models/readarr_models_tests.rs b/src/models/readarr_models_tests.rs index fe163400..20ffb5b5 100644 --- a/src/models/readarr_models_tests.rs +++ b/src/models/readarr_models_tests.rs @@ -2,7 +2,7 @@ mod tests { use chrono::Utc; use pretty_assertions::{assert_eq, assert_str_eq}; - use serde_json::json; + use serde_json::{Value, json}; use crate::models::readarr_models::{ AddAuthorSearchResult, BlocklistItem, BlocklistResponse, Book, BookFile, DownloadRecord, @@ -758,6 +758,126 @@ mod tests { ); } + #[test] + fn test_readarr_history_event_type_deserialization() { + assert_eq!( + deserialize_history_event_type(json!("unknown")), + ReadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!("grabbed")), + ReadarrHistoryEventType::Grabbed + ); + assert_eq!( + deserialize_history_event_type(json!("authorFolderImported")), + ReadarrHistoryEventType::AuthorFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("bookImportIncomplete")), + ReadarrHistoryEventType::BookImportIncomplete + ); + assert_eq!( + deserialize_history_event_type(json!("downloadIgnored")), + ReadarrHistoryEventType::DownloadIgnored + ); + assert_eq!( + deserialize_history_event_type(json!("downloadImported")), + ReadarrHistoryEventType::DownloadImported + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFailed")), + ReadarrHistoryEventType::DownloadFailed + ); + assert_eq!( + deserialize_history_event_type(json!("bookFileDeleted")), + ReadarrHistoryEventType::BookFileDeleted + ); + assert_eq!( + deserialize_history_event_type(json!("bookFileImported")), + ReadarrHistoryEventType::BookFileImported + ); + assert_eq!( + deserialize_history_event_type(json!("bookFileRenamed")), + ReadarrHistoryEventType::BookFileRenamed + ); + assert_eq!( + deserialize_history_event_type(json!("bookFileRetagged")), + ReadarrHistoryEventType::BookFileRetagged + ); + } + + #[test] + fn test_readarr_history_event_type_deserialization_falls_back_to_unknown() { + assert_eq!( + deserialize_history_event_type(json!("bookImportPartiallyComplete")), + ReadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(2)), + ReadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(11)), + ReadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(null)), + ReadarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!({ "id": 2 })), + ReadarrHistoryEventType::Unknown + ); + } + + #[test] + fn test_readarr_history_event_type_serialization() { + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::Unknown), + "unknown" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::Grabbed), + "grabbed" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::AuthorFolderImported), + "authorFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::BookImportIncomplete), + "bookImportIncomplete" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::DownloadIgnored), + "downloadIgnored" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::DownloadImported), + "downloadImported" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::DownloadFailed), + "downloadFailed" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::BookFileDeleted), + "bookFileDeleted" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::BookFileImported), + "bookFileImported" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::BookFileRenamed), + "bookFileRenamed" + ); + assert_str_eq!( + serialize_history_event_type(ReadarrHistoryEventType::BookFileRetagged), + "bookFileRetagged" + ); + } + #[test] fn test_readarr_task_name_display() { assert_str_eq!( @@ -1019,4 +1139,31 @@ mod tests { assert!(search_result.genres.is_empty()); assert_none!(&search_result.ratings); } + + fn deserialize_history_event_type(event_type: Value) -> ReadarrHistoryEventType { + let history_item_json = json!({ + "id": 1, + "authorId": 1, + "bookId": 1, + "sourceTitle": "Test Source Title", + "date": "2024-01-01T00:00:00Z", + "eventType": event_type + }); + + serde_json::from_value::(history_item_json) + .unwrap() + .event_type + } + + fn serialize_history_event_type(event_type: ReadarrHistoryEventType) -> String { + let history_item = ReadarrHistoryItem { + event_type, + ..ReadarrHistoryItem::default() + }; + + serde_json::to_value(history_item).unwrap()["eventType"] + .as_str() + .unwrap() + .to_owned() + } } diff --git a/src/models/sonarr_models.rs b/src/models/sonarr_models.rs index 1b139543..11ee02d2 100644 --- a/src/models/sonarr_models.rs +++ b/src/models/sonarr_models.rs @@ -499,6 +499,7 @@ pub struct SonarrHistoryItem { pub quality: QualityWrapper, pub languages: Vec>, pub date: DateTime, + #[serde(deserialize_with = "super::from_json_or_default")] pub event_type: SonarrHistoryEventType, pub data: SonarrHistoryData, } diff --git a/src/models/sonarr_models_tests.rs b/src/models/sonarr_models_tests.rs index d7ea2026..8e82559a 100644 --- a/src/models/sonarr_models_tests.rs +++ b/src/models/sonarr_models_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; - use serde_json::json; + use serde_json::{Value, json}; use crate::models::{ Serdeable, @@ -213,6 +213,102 @@ mod tests { ); } + #[test] + fn test_sonarr_history_event_type_deserialization() { + assert_eq!( + deserialize_history_event_type(json!("unknown")), + SonarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!("grabbed")), + SonarrHistoryEventType::Grabbed + ); + assert_eq!( + deserialize_history_event_type(json!("seriesFolderImported")), + SonarrHistoryEventType::SeriesFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFolderImported")), + SonarrHistoryEventType::DownloadFolderImported + ); + assert_eq!( + deserialize_history_event_type(json!("downloadFailed")), + SonarrHistoryEventType::DownloadFailed + ); + assert_eq!( + deserialize_history_event_type(json!("episodeFileDeleted")), + SonarrHistoryEventType::EpisodeFileDeleted + ); + assert_eq!( + deserialize_history_event_type(json!("episodeFileRenamed")), + SonarrHistoryEventType::EpisodeFileRenamed + ); + assert_eq!( + deserialize_history_event_type(json!("downloadIgnored")), + SonarrHistoryEventType::DownloadIgnored + ); + } + + #[test] + fn test_sonarr_history_event_type_deserialization_falls_back_to_unknown() { + assert_eq!( + deserialize_history_event_type(json!("episodeFileImportFailed")), + SonarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(2)), + SonarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(11)), + SonarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!(null)), + SonarrHistoryEventType::Unknown + ); + assert_eq!( + deserialize_history_event_type(json!({ "id": 2 })), + SonarrHistoryEventType::Unknown + ); + } + + #[test] + fn test_sonarr_history_event_type_serialization() { + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::Unknown), + "unknown" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::Grabbed), + "grabbed" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::SeriesFolderImported), + "seriesFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::DownloadFolderImported), + "downloadFolderImported" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::DownloadFailed), + "downloadFailed" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::EpisodeFileDeleted), + "episodeFileDeleted" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::EpisodeFileRenamed), + "episodeFileRenamed" + ); + assert_str_eq!( + serialize_history_event_type(SonarrHistoryEventType::DownloadIgnored), + "downloadIgnored" + ); + } + #[test] fn test_task_name_display() { assert_str_eq!( @@ -601,4 +697,33 @@ mod tests { SonarrSerdeable::IndexerTestResults(indexer_test_results) ); } + + fn deserialize_history_event_type(event_type: Value) -> SonarrHistoryEventType { + let history_item_json = json!({ + "id": 1, + "sourceTitle": "Test Source Title", + "episodeId": 1, + "quality": { "quality": { "name": "HDTV - 1080p" } }, + "languages": [], + "date": "2024-01-01T00:00:00Z", + "eventType": event_type, + "data": {} + }); + + serde_json::from_value::(history_item_json) + .unwrap() + .event_type + } + + fn serialize_history_event_type(event_type: SonarrHistoryEventType) -> String { + let history_item = SonarrHistoryItem { + event_type, + ..SonarrHistoryItem::default() + }; + + serde_json::to_value(history_item).unwrap()["eventType"] + .as_str() + .unwrap() + .to_owned() + } }