Lidarr support #1

Merged
Dark-Alex-17 merged 61 commits from lidarr into main 2026-01-21 21:30:47 +00:00
12 changed files with 583 additions and 12 deletions
Showing only changes of commit 5afee1998b - Show all commits
+5 -1
View File
@@ -7,7 +7,11 @@ use crate::models::Route;
#[path = "lidarr_context_clues_tests.rs"]
mod lidarr_context_clues_tests;
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 6] = [
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 7] = [
(
DEFAULT_KEYBINDINGS.toggle_monitoring,
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
),
(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc),
(DEFAULT_KEYBINDINGS.delete, DEFAULT_KEYBINDINGS.delete.desc),
(DEFAULT_KEYBINDINGS.search, DEFAULT_KEYBINDINGS.search.desc),
@@ -13,6 +13,13 @@ mod tests {
fn test_artists_context_clues() {
let mut artists_context_clues_iter = ARTISTS_CONTEXT_CLUES.iter();
assert_some_eq_x!(
artists_context_clues_iter.next(),
&(
DEFAULT_KEYBINDINGS.toggle_monitoring,
DEFAULT_KEYBINDINGS.toggle_monitoring.desc
)
);
assert_some_eq_x!(
artists_context_clues_iter.next(),
&(DEFAULT_KEYBINDINGS.sort, DEFAULT_KEYBINDINGS.sort.desc)
+55
View File
@@ -19,6 +19,8 @@ mod tests {
mod cli {
use super::*;
use clap::error::ErrorKind;
use pretty_assertions::assert_eq;
#[test]
fn test_list_artists_has_no_arg_requirements() {
@@ -40,6 +42,31 @@ mod tests {
assert_err!(&result);
}
#[test]
fn test_toggle_artist_monitoring_requires_artist_id() {
let result =
Cli::command().try_get_matches_from(["managarr", "lidarr", "toggle-artist-monitoring"]);
assert_err!(&result);
assert_eq!(
result.unwrap_err().kind(),
ErrorKind::MissingRequiredArgument
);
}
#[test]
fn test_toggle_artist_monitoring_requirements_satisfied() {
let result = Cli::command().try_get_matches_from([
"managarr",
"lidarr",
"toggle-artist-monitoring",
"--artist-id",
"1",
]);
assert_ok!(&result);
}
}
mod handler {
@@ -119,5 +146,33 @@ mod tests {
assert_ok!(&result);
}
#[tokio::test]
async fn test_toggle_artist_monitoring_command() {
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
LidarrEvent::ToggleArtistMonitoring(1).into(),
))
.times(1)
.returning(|_| {
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
json!({"testResponse": "response"}),
)))
});
let app_arc = Arc::new(Mutex::new(App::test_default()));
let toggle_artist_monitoring_command = LidarrCommand::ToggleArtistMonitoring { artist_id: 1 };
let result = LidarrCliHandler::with(
&app_arc,
toggle_artist_monitoring_command,
&mut mock_network,
)
.handle()
.await;
assert_ok!(&result);
}
}
}
+20 -1
View File
@@ -1,11 +1,12 @@
use std::sync::Arc;
use anyhow::Result;
use clap::Subcommand;
use clap::{Subcommand, arg};
use delete_command_handler::{LidarrDeleteCommand, LidarrDeleteCommandHandler};
use list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
use tokio::sync::Mutex;
use crate::network::lidarr_network::LidarrEvent;
use crate::{app::App, network::NetworkTrait};
use super::{CliCommandHandler, Command};
@@ -29,6 +30,17 @@ pub enum LidarrCommand {
about = "Commands to list attributes from your Lidarr instance"
)]
List(LidarrListCommand),
#[command(
about = "Toggle monitoring for the specified artist corresponding to the given artist ID"
)]
ToggleArtistMonitoring {
#[arg(
long,
help = "The Lidarr ID of the artist to toggle monitoring on",
required = true
)]
artist_id: i64,
},
}
impl From<LidarrCommand> for Command {
@@ -68,6 +80,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, '
.handle()
.await?
}
LidarrCommand::ToggleArtistMonitoring { artist_id } => {
let resp = self
.network
.handle_network_event(LidarrEvent::ToggleArtistMonitoring(artist_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
};
Ok(result)
@@ -6,10 +6,14 @@ mod tests {
use serde_json::Number;
use strum::IntoEnumIterator;
use crate::app::App;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::assert_modal_absent;
use crate::handlers::KeyEventHandler;
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
use crate::network::lidarr_network::LidarrEvent;
#[test]
fn test_library_handler_accepts() {
@@ -214,6 +218,55 @@ mod tests {
assert_str_eq!(sort_option.name, "Tags");
}
#[test]
fn test_toggle_monitoring_key() {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app.is_routing = false;
LibraryHandler::new(
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
&mut app,
ActiveLidarrBlock::Artists,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
assert!(app.data.lidarr_data.prompt_confirm);
assert!(app.is_routing);
assert_some_eq_x!(
&app.data.lidarr_data.prompt_confirm_action,
&LidarrEvent::ToggleArtistMonitoring(0)
);
}
#[test]
fn test_toggle_monitoring_key_no_op_when_not_ready() {
let mut app = App::test_default();
app.is_loading = true;
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app.is_routing = false;
LibraryHandler::new(
DEFAULT_KEYBINDINGS.toggle_monitoring.key,
&mut app,
ActiveLidarrBlock::Artists,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
assert!(!app.data.lidarr_data.prompt_confirm);
assert_modal_absent!(app.data.lidarr_data.prompt_confirm_action);
assert!(!app.is_routing);
}
fn artists_vec() -> Vec<Artist> {
vec![
Artist {
+24 -2
View File
@@ -11,6 +11,7 @@ use crate::{
},
stateful_table::SortOption,
},
network::lidarr_network::LidarrEvent,
};
use super::handle_change_tab_left_right_keys;
@@ -31,6 +32,12 @@ pub(super) struct LibraryHandler<'a, 'b> {
_context: Option<ActiveLidarrBlock>,
}
impl LibraryHandler<'_, '_> {
fn extract_artist_id(&self) -> i64 {
self.app.data.lidarr_data.artists.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, 'b> {
fn handle(&mut self) {
let artists_table_handling_config = TableHandlingConfig::new(ActiveLidarrBlock::Artists.into())
@@ -114,8 +121,23 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
fn handle_char_key_event(&mut self) {
let key = self.key;
if self.active_lidarr_block == ActiveLidarrBlock::Artists && matches_key!(refresh, key) {
self.app.should_refresh = true;
if self.active_lidarr_block == ActiveLidarrBlock::Artists {
match key {
_ if matches_key!(toggle_monitoring, key) => {
self.app.data.lidarr_data.prompt_confirm = true;
self.app.data.lidarr_data.prompt_confirm_action = Some(
LidarrEvent::ToggleArtistMonitoring(self.extract_artist_id()),
);
self
.app
.pop_and_push_navigation_stack(self.active_lidarr_block.into());
}
_ if matches_key!(refresh, key) => {
self.app.should_refresh = true;
}
_ => (),
}
}
}
+35 -5
View File
@@ -5,7 +5,10 @@ use serde::{Deserialize, Serialize};
use serde_json::{Number, Value};
use strum::{Display, EnumIter};
use super::{HorizontallyScrollableText, Serdeable};
use super::{
HorizontallyScrollableText, Serdeable,
servarr_models::{DiskSpace, QualityProfile, RootFolder, Tag},
};
use crate::serde_enum_from;
#[cfg(test)]
@@ -29,6 +32,7 @@ pub struct Artist {
#[serde(deserialize_with = "super::from_i64")]
pub metadata_profile_id: i64,
pub monitored: bool,
pub monitor_new_items: NewItemMonitorType,
pub genres: Vec<String>,
pub tags: Vec<Number>,
pub added: DateTime<Utc>,
@@ -94,6 +98,31 @@ impl From<(&i64, &String)> for MetadataProfile {
}
}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum NewItemMonitorType {
#[default]
#[display_style(name = "All Albums")]
All,
#[display_style(name = "No New Albums")]
None,
#[display_style(name = "New Albums")]
New,
}
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadRecord {
@@ -174,14 +203,15 @@ impl From<LidarrSerdeable> for Serdeable {
serde_enum_from!(
LidarrSerdeable {
Artist(Artist),
Artists(Vec<Artist>),
DiskSpaces(Vec<super::servarr_models::DiskSpace>),
DiskSpaces(Vec<DiskSpace>),
DownloadsResponse(DownloadsResponse),
MetadataProfiles(Vec<MetadataProfile>),
QualityProfiles(Vec<super::servarr_models::QualityProfile>),
RootFolders(Vec<super::servarr_models::RootFolder>),
QualityProfiles(Vec<QualityProfile>),
RootFolders(Vec<RootFolder>),
SystemStatus(SystemStatus),
Tags(Vec<super::servarr_models::Tag>),
Tags(Vec<Tag>),
Value(Value),
}
);
+185
View File
@@ -1,8 +1,14 @@
#[cfg(test)]
mod tests {
use chrono::Utc;
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::json;
use crate::models::lidarr_models::{
DownloadRecord, DownloadStatus, DownloadsResponse, MetadataProfile, NewItemMonitorType,
SystemStatus,
};
use crate::models::servarr_models::{DiskSpace, QualityProfile, RootFolder, Tag};
use crate::models::{
Serdeable,
lidarr_models::{Artist, ArtistStatistics, ArtistStatus, LidarrSerdeable, Ratings},
@@ -13,6 +19,20 @@ mod tests {
assert_eq!(ArtistStatus::default(), ArtistStatus::Continuing);
}
#[test]
fn test_new_item_monitor_type_display() {
assert_str_eq!(NewItemMonitorType::All.to_string(), "all");
assert_str_eq!(NewItemMonitorType::None.to_string(), "none");
assert_str_eq!(NewItemMonitorType::New.to_string(), "new");
}
#[test]
fn test_new_item_monitor_type_to_display_str() {
assert_str_eq!(NewItemMonitorType::All.to_display_str(), "All Albums");
assert_str_eq!(NewItemMonitorType::None.to_display_str(), "No New Albums");
assert_str_eq!(NewItemMonitorType::New.to_display_str(), "New Albums");
}
#[test]
fn test_lidarr_serdeable_from() {
let lidarr_serdeable = LidarrSerdeable::Value(json!({}));
@@ -65,6 +85,7 @@ mod tests {
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": ["Rock", "Alternative"],
"tags": [1, 2],
"added": "2023-01-01T00:00:00Z",
@@ -95,6 +116,7 @@ mod tests {
assert_eq!(artist.quality_profile_id, 1);
assert_eq!(artist.metadata_profile_id, 1);
assert!(artist.monitored);
assert_eq!(artist.monitor_new_items, NewItemMonitorType::All);
assert_eq!(artist.genres, vec!["Rock", "Alternative"]);
assert_eq!(artist.tags.len(), 2);
assert_some!(&artist.ratings);
@@ -184,6 +206,7 @@ mod tests {
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": false,
"monitorNewItems": "all",
"genres": [],
"tags": [],
"added": "2023-01-01T00:00:00Z"
@@ -194,7 +217,169 @@ mod tests {
assert_none!(&artist.overview);
assert_none!(&artist.artist_type);
assert_none!(&artist.disambiguation);
assert_eq!(artist.monitor_new_items, NewItemMonitorType::All);
assert_none!(&artist.ratings);
assert_none!(&artist.statistics);
}
#[test]
fn test_lidarr_serdeable_from_artist() {
let artist = Artist {
id: 1,
..Artist::default()
};
let lidarr_serdeable: LidarrSerdeable = artist.clone().into();
assert_eq!(lidarr_serdeable, LidarrSerdeable::Artist(artist));
}
#[test]
fn test_lidarr_serdeable_from_disk_spaces() {
let disk_spaces = vec![DiskSpace {
free_space: 1,
total_space: 1,
}];
let lidarr_serdeable: LidarrSerdeable = disk_spaces.clone().into();
assert_eq!(lidarr_serdeable, LidarrSerdeable::DiskSpaces(disk_spaces));
}
#[test]
fn test_lidarr_serdeable_from_downloads_response() {
let downloads_response = DownloadsResponse {
records: vec![DownloadRecord {
id: 1,
..DownloadRecord::default()
}],
};
let lidarr_serdeable: LidarrSerdeable = downloads_response.clone().into();
assert_eq!(
lidarr_serdeable,
LidarrSerdeable::DownloadsResponse(downloads_response)
);
}
#[test]
fn test_lidarr_serdeable_from_metadata_profiles() {
let metadata_profiles = vec![MetadataProfile {
id: 1,
name: "Standard".to_owned(),
}];
let lidarr_serdeable: LidarrSerdeable = metadata_profiles.clone().into();
assert_eq!(
lidarr_serdeable,
LidarrSerdeable::MetadataProfiles(metadata_profiles)
);
}
#[test]
fn test_lidarr_serdeable_from_quality_profiles() {
let quality_profiles = vec![QualityProfile {
id: 1,
name: "Any".to_owned(),
}];
let lidarr_serdeable: LidarrSerdeable = quality_profiles.clone().into();
assert_eq!(
lidarr_serdeable,
LidarrSerdeable::QualityProfiles(quality_profiles)
);
}
#[test]
fn test_lidarr_serdeable_from_root_folders() {
let root_folders = vec![RootFolder {
id: 1,
path: "/music".to_owned(),
accessible: true,
free_space: 1000000,
unmapped_folders: None,
}];
let lidarr_serdeable: LidarrSerdeable = root_folders.clone().into();
assert_eq!(lidarr_serdeable, LidarrSerdeable::RootFolders(root_folders));
}
#[test]
fn test_lidarr_serdeable_from_system_status() {
let system_status = SystemStatus {
version: "1.0.0".to_owned(),
start_time: Utc::now(),
};
let lidarr_serdeable: LidarrSerdeable = system_status.clone().into();
assert_eq!(
lidarr_serdeable,
LidarrSerdeable::SystemStatus(system_status)
);
}
#[test]
fn test_lidarr_serdeable_from_tags() {
let tags = vec![Tag {
id: 1,
label: "rock".to_owned(),
}];
let lidarr_serdeable: LidarrSerdeable = tags.clone().into();
assert_eq!(lidarr_serdeable, LidarrSerdeable::Tags(tags));
}
#[test]
fn test_artist_status_display() {
assert_str_eq!(ArtistStatus::Continuing.to_string(), "continuing");
assert_str_eq!(ArtistStatus::Ended.to_string(), "ended");
assert_str_eq!(ArtistStatus::Deleted.to_string(), "deleted");
}
#[test]
fn test_artist_status_to_display_str() {
assert_str_eq!(ArtistStatus::Continuing.to_display_str(), "Continuing");
assert_str_eq!(ArtistStatus::Ended.to_display_str(), "Ended");
assert_str_eq!(ArtistStatus::Deleted.to_display_str(), "Deleted");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
}
@@ -3,6 +3,7 @@ mod tests {
use crate::models::lidarr_models::{Artist, DeleteArtistParams, LidarrSerdeable};
use crate::network::lidarr_network::LidarrEvent;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use mockito::Matcher;
use pretty_assertions::assert_eq;
use serde_json::json;
@@ -18,6 +19,7 @@ mod tests {
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": [],
"tags": [],
"added": "2023-01-01T00:00:00Z"
@@ -66,4 +68,88 @@ mod tests {
async_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_get_artist_details_event() {
let artist_json = json!({
"id": 1,
"mbId": "test-mb-id",
"artistName": "Test Artist",
"foreignArtistId": "test-foreign-id",
"status": "continuing",
"path": "/music/test-artist",
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": [],
"tags": [],
"added": "2023-01-01T00:00:00Z"
});
let response: Artist = serde_json::from_value(artist_json.clone()).unwrap();
let (mock, app, _server) = MockServarrApi::get()
.returns(artist_json)
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let result = network
.handle_lidarr_event(LidarrEvent::GetArtistDetails(1))
.await;
mock.assert_async().await;
let LidarrSerdeable::Artist(artist) = result.unwrap() else {
panic!("Expected Artist");
};
assert_eq!(artist, response);
}
#[tokio::test]
async fn test_handle_toggle_artist_monitoring_event() {
let artist_json = json!({
"id": 1,
"mbId": "test-mb-id",
"artistName": "Test Artist",
"foreignArtistId": "test-foreign-id",
"status": "continuing",
"path": "/music/test-artist",
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": [],
"tags": [],
"added": "2023-01-01T00:00:00Z"
});
let mut expected_body = artist_json.clone();
*expected_body.get_mut("monitored").unwrap() = json!(false);
let (get_mock, app, mut server) = MockServarrApi::get()
.returns(artist_json)
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
let put_mock = server
.mock("PUT", "/api/v1/artist/1")
.match_body(Matcher::Json(expected_body))
.match_header("X-Api-Key", "test1234")
.with_status(202)
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::ToggleArtistMonitoring(1))
.await
.is_ok()
);
get_mock.assert_async().await;
put_mock.assert_async().await;
}
}
+87 -1
View File
@@ -1,5 +1,6 @@
use anyhow::Result;
use log::info;
use log::{debug, info, warn};
use serde_json::{Value, json};
use crate::models::Route;
use crate::models::lidarr_models::{Artist, DeleteArtistParams};
@@ -65,4 +66,89 @@ impl Network<'_, '_> {
})
.await
}
pub(in crate::network::lidarr_network) async fn get_artist_details(
&mut self,
artist_id: i64,
) -> Result<Artist> {
info!("Fetching details for Lidarr artist with ID: {artist_id}");
let event = LidarrEvent::GetArtistDetails(artist_id);
let request_props = self
.request_props_from(
event,
RequestMethod::Get,
None::<()>,
Some(format!("/{artist_id}")),
None,
)
.await;
self
.handle_request::<(), Artist>(request_props, |_, _| ())
.await
}
pub(in crate::network::lidarr_network) async fn toggle_artist_monitoring(
&mut self,
artist_id: i64,
) -> Result<()> {
let event = LidarrEvent::ToggleArtistMonitoring(artist_id);
let detail_event = LidarrEvent::GetArtistDetails(artist_id);
info!("Toggling artist monitoring for artist with ID: {artist_id}");
info!("Fetching artist details for artist with ID: {artist_id}");
let request_props = self
.request_props_from(
detail_event,
RequestMethod::Get,
None::<()>,
Some(format!("/{artist_id}")),
None,
)
.await;
let mut response = String::new();
self
.handle_request::<(), Value>(request_props, |detailed_artist_body, _| {
response = detailed_artist_body.to_string()
})
.await?;
info!("Constructing toggle artist monitoring body");
match serde_json::from_str::<Value>(&response) {
Ok(mut detailed_artist_body) => {
let monitored = detailed_artist_body
.get("monitored")
.unwrap()
.as_bool()
.unwrap();
*detailed_artist_body.get_mut("monitored").unwrap() = json!(!monitored);
debug!("Toggle artist monitoring body: {detailed_artist_body:?}");
let request_props = self
.request_props_from(
event,
RequestMethod::Put,
Some(detailed_artist_body),
Some(format!("/{artist_id}")),
None,
)
.await;
self
.handle_request::<Value, ()>(request_props, |_, _| ())
.await
}
Err(_) => {
warn!("Request for detailed artist body was interrupted");
Ok(())
}
}
}
}
@@ -8,6 +8,18 @@ mod tests {
use rstest::rstest;
use serde_json::json;
#[rstest]
fn test_resource_artist(
#[values(
LidarrEvent::GetArtistDetails(0),
LidarrEvent::ListArtists,
LidarrEvent::ToggleArtistMonitoring(0)
)]
event: LidarrEvent,
) {
assert_str_eq!(event.resource(), "/artist");
}
#[rstest]
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
#[case(LidarrEvent::GetDownloads(500), "/queue")]
@@ -17,7 +29,6 @@ mod tests {
#[case(LidarrEvent::GetStatus, "/system/status")]
#[case(LidarrEvent::GetTags, "/tag")]
#[case(LidarrEvent::HealthCheck, "/health")]
#[case(LidarrEvent::ListArtists, "/artist")]
fn test_resource(#[case] event: LidarrEvent, #[case] expected_uri: &str) {
assert_str_eq!(event.resource(), expected_uri);
}
+14 -1
View File
@@ -18,6 +18,7 @@ mod lidarr_network_tests;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum LidarrEvent {
DeleteArtist(DeleteArtistParams),
GetArtistDetails(i64),
GetDiskSpace,
GetDownloads(u64),
GetMetadataProfiles,
@@ -27,12 +28,16 @@ pub enum LidarrEvent {
GetTags,
HealthCheck,
ListArtists,
ToggleArtistMonitoring(i64),
}
impl NetworkResource for LidarrEvent {
fn resource(&self) -> &'static str {
match &self {
LidarrEvent::DeleteArtist(_) | LidarrEvent::ListArtists => "/artist",
LidarrEvent::DeleteArtist(_)
| LidarrEvent::GetArtistDetails(_)
| LidarrEvent::ListArtists
| LidarrEvent::ToggleArtistMonitoring(_) => "/artist",
LidarrEvent::GetDiskSpace => "/diskspace",
LidarrEvent::GetDownloads(_) => "/queue",
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
@@ -60,6 +65,10 @@ impl Network<'_, '_> {
LidarrEvent::DeleteArtist(params) => {
self.delete_artist(params).await.map(LidarrSerdeable::from)
}
LidarrEvent::GetArtistDetails(artist_id) => self
.get_artist_details(artist_id)
.await
.map(LidarrSerdeable::from),
LidarrEvent::GetDiskSpace => self.get_lidarr_diskspace().await.map(LidarrSerdeable::from),
LidarrEvent::GetDownloads(count) => self
.get_lidarr_downloads(count)
@@ -84,6 +93,10 @@ impl Network<'_, '_> {
.await
.map(LidarrSerdeable::from),
LidarrEvent::ListArtists => self.list_artists().await.map(LidarrSerdeable::from),
LidarrEvent::ToggleArtistMonitoring(artist_id) => self
.toggle_artist_monitoring(artist_id)
.await
.map(LidarrSerdeable::from),
}
}