feat(cli): Added a spinner to the CLI for long running commands like fetching releases

This commit is contained in:
2024-11-20 19:33:40 -07:00
parent f5631376af
commit 34157ef32f
19 changed files with 717 additions and 271 deletions
+69 -13
View File
@@ -14,7 +14,7 @@ use super::{
HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper, QueueEvent,
Release, SecurityConfig,
},
HorizontallyScrollableText, Serdeable,
EnumDisplayStyle, HorizontallyScrollableText, Serdeable,
};
#[cfg(test)]
@@ -43,7 +43,7 @@ pub struct BlocklistResponse {
pub records: Vec<BlocklistItem>,
}
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadRecord {
pub title: String,
@@ -52,16 +52,18 @@ pub struct DownloadRecord {
pub id: i64,
#[serde(deserialize_with = "super::from_i64")]
pub episode_id: i64,
#[serde(deserialize_with = "super::from_i64")]
pub size: i64,
#[serde(deserialize_with = "super::from_i64")]
pub sizeleft: i64,
#[serde(deserialize_with = "super::from_f64")]
pub size: f64,
#[serde(deserialize_with = "super::from_f64")]
pub sizeleft: f64,
pub output_path: Option<HorizontallyScrollableText>,
#[serde(default)]
pub indexer: String,
pub download_client: String,
}
impl Eq for DownloadRecord {}
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
@@ -242,8 +244,8 @@ impl Display for SeriesType {
}
}
impl SeriesType {
pub fn to_display_str<'a>(self) -> &'a str {
impl<'a> EnumDisplayStyle<'a> for SeriesType {
fn to_display_str(self) -> &'a str {
match self {
SeriesType::Standard => "Standard",
SeriesType::Daily => "Daily",
@@ -293,8 +295,8 @@ impl Display for SeriesStatus {
}
}
impl SeriesStatus {
pub fn to_display_str<'a>(self) -> &'a str {
impl<'a> EnumDisplayStyle<'a> for SeriesStatus {
fn to_display_str(self) -> &'a str {
match self {
SeriesStatus::Continuing => "Continuing",
SeriesStatus::Ended => "Ended",
@@ -313,8 +315,62 @@ pub struct SonarrHistoryWrapper {
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SonarrHistoryData {
pub dropped_path: String,
pub imported_path: String,
pub dropped_path: Option<String>,
pub imported_path: Option<String>,
pub indexer: Option<String>,
pub release_group: Option<String>,
pub series_match_type: Option<String>,
pub nzb_info_url: Option<String>,
pub download_client_name: Option<String>,
pub age: Option<String>,
pub published_date: Option<DateTime<Utc>>,
pub message: Option<String>,
pub reason: Option<String>,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SonarrHistoryEventType {
#[default]
Unknown,
Grabbed,
SeriesFolderImported,
DownloadFolderImported,
DownloadFailed,
EpisodeFileDeleted,
EpisodeFileRenamed,
DownloadIgnored,
}
impl Display for SonarrHistoryEventType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let event_type = match self {
SonarrHistoryEventType::Unknown => "unknown",
SonarrHistoryEventType::Grabbed => "grabbed",
SonarrHistoryEventType::SeriesFolderImported => "seriesFolderImported",
SonarrHistoryEventType::DownloadFolderImported => "downloadFolderImported",
SonarrHistoryEventType::DownloadFailed => "downloadFailed",
SonarrHistoryEventType::EpisodeFileDeleted => "episodeFileDeleted",
SonarrHistoryEventType::EpisodeFileRenamed => "episodeFileRenamed",
SonarrHistoryEventType::DownloadIgnored => "downloadIgnored",
};
write!(f, "{event_type}")
}
}
impl<'a> EnumDisplayStyle<'a> for SonarrHistoryEventType {
fn to_display_str(self) -> &'a str {
match self {
SonarrHistoryEventType::Unknown => "Unknown",
SonarrHistoryEventType::Grabbed => "Grabbed",
SonarrHistoryEventType::SeriesFolderImported => "Series Folder Imported",
SonarrHistoryEventType::DownloadFolderImported => "Download Folder Imported",
SonarrHistoryEventType::DownloadFailed => "Download Failed",
SonarrHistoryEventType::EpisodeFileDeleted => "Episode File Deleted",
SonarrHistoryEventType::EpisodeFileRenamed => "Episode File Renamed",
SonarrHistoryEventType::DownloadIgnored => "Download Ignored",
}
}
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
@@ -326,7 +382,7 @@ pub struct SonarrHistoryItem {
#[serde(deserialize_with = "super::from_i64")]
pub episode_id: i64,
pub quality: QualityWrapper,
pub languages: Vec<Language>,
pub language: Language,
pub date: DateTime<Utc>,
pub event_type: String,
pub data: SonarrHistoryData,
+63 -3
View File
@@ -9,10 +9,10 @@ mod tests {
},
sonarr_models::{
BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse, Episode,
IndexerSettings, Series, SeriesStatus, SeriesType, SonarrHistoryItem, SonarrSerdeable,
SystemStatus,
IndexerSettings, Series, SeriesStatus, SeriesType, SonarrHistoryEventType, SonarrHistoryItem,
SonarrSerdeable, SystemStatus,
},
Serdeable,
EnumDisplayStyle, Serdeable,
};
#[test]
@@ -56,6 +56,66 @@ mod tests {
assert_str_eq!(SeriesType::Anime.to_display_str(), "Anime");
}
#[test]
fn test_sonarr_history_event_type_display() {
assert_str_eq!(SonarrHistoryEventType::Unknown.to_string(), "unknown",);
assert_str_eq!(SonarrHistoryEventType::Grabbed.to_string(), "grabbed",);
assert_str_eq!(
SonarrHistoryEventType::SeriesFolderImported.to_string(),
"seriesFolderImported",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadFolderImported.to_string(),
"downloadFolderImported",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadFailed.to_string(),
"downloadFailed",
);
assert_str_eq!(
SonarrHistoryEventType::EpisodeFileDeleted.to_string(),
"episodeFileDeleted",
);
assert_str_eq!(
SonarrHistoryEventType::EpisodeFileRenamed.to_string(),
"episodeFileRenamed",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadIgnored.to_string(),
"downloadIgnored",
);
}
#[test]
fn test_sonarr_history_event_type_to_display_str() {
assert_str_eq!(SonarrHistoryEventType::Unknown.to_display_str(), "Unknown",);
assert_str_eq!(SonarrHistoryEventType::Grabbed.to_display_str(), "Grabbed",);
assert_str_eq!(
SonarrHistoryEventType::SeriesFolderImported.to_display_str(),
"Series Folder Imported",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadFolderImported.to_display_str(),
"Download Folder Imported",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadFailed.to_display_str(),
"Download Failed",
);
assert_str_eq!(
SonarrHistoryEventType::EpisodeFileDeleted.to_display_str(),
"Episode File Deleted",
);
assert_str_eq!(
SonarrHistoryEventType::EpisodeFileRenamed.to_display_str(),
"Episode File Renamed",
);
assert_str_eq!(
SonarrHistoryEventType::DownloadIgnored.to_display_str(),
"Download Ignored",
);
}
#[test]
fn test_sonarr_serdeable_from() {
let sonarr_serdeable = SonarrSerdeable::Value(json!({}));