diff --git a/Cargo.toml b/Cargo.toml index 594d3f6..724f915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,8 @@ serde_json = "1.0.91" serde = { version = "1.0", features = ["derive"] } strum = {version = "0.25.0", features = ["derive"] } strum_macros = "0.25.0" -tokio = { version = "1.24.1", features = ["full"] } +tokio = { version = "1.29.0", features = ["full"] } +tokio-util = "0.7.8" tui = { version = "0.21.0", package = "ratatui", features = ["all-widgets"] } urlencoding = "2.1.2" diff --git a/src/app/app_tests.rs b/src/app/app_tests.rs index aa07add..d015d0c 100644 --- a/src/app/app_tests.rs +++ b/src/app/app_tests.rs @@ -16,6 +16,7 @@ mod tests { assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]); assert!(app.network_tx.is_none()); + assert!(!app.cancellation_token.is_cancelled()); assert_eq!(app.error, HorizontallyScrollableText::default()); assert!(app.response.is_empty()); assert_eq!(app.server_tabs.index, 0); @@ -82,6 +83,19 @@ mod tests { assert!(app.is_routing); } + #[test] + fn test_reset_cancellation_token() { + let mut app = App::default(); + app.cancellation_token.cancel(); + + assert!(app.cancellation_token.is_cancelled()); + + let new_token = app.reset_cancellation_token(); + + assert!(!app.cancellation_token.is_cancelled()); + assert!(!new_token.is_cancelled()); + } + #[test] fn test_reset_tick_count() { let mut app = App { diff --git a/src/app/mod.rs b/src/app/mod.rs index be32f1e..4a0ddc7 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -3,6 +3,7 @@ use log::{debug, error}; use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::Sender; use tokio::time::Instant; +use tokio_util::sync::CancellationToken; use crate::app::radarr::{ActiveRadarrBlock, RadarrData}; use crate::models::{HorizontallyScrollableText, Route, TabRoute, TabState}; @@ -19,6 +20,7 @@ const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None); pub struct App<'a> { navigation_stack: Vec, network_tx: Option>, + cancellation_token: CancellationToken, pub server_tabs: TabState, pub error: HorizontallyScrollableText, pub response: String, @@ -36,10 +38,15 @@ pub struct App<'a> { } impl<'a> App<'a> { - pub fn new(network_tx: Sender, config: AppConfig) -> Self { + pub fn new( + network_tx: Sender, + config: AppConfig, + cancellation_token: CancellationToken, + ) -> Self { App { network_tx: Some(network_tx), config, + cancellation_token, ..App::default() } } @@ -102,6 +109,12 @@ impl<'a> App<'a> { } } + pub fn reset_cancellation_token(&mut self) -> CancellationToken { + self.cancellation_token = CancellationToken::new(); + + self.cancellation_token.clone() + } + pub fn pop_and_push_navigation_stack(&mut self, route: Route) { self.pop_navigation_stack(); self.push_navigation_stack(route); @@ -117,6 +130,7 @@ impl<'a> Default for App<'a> { App { navigation_stack: vec![DEFAULT_ROUTE], network_tx: None, + cancellation_token: CancellationToken::new(), error: HorizontallyScrollableText::default(), response: String::default(), server_tabs: TabState::new(vec![ diff --git a/src/app/radarr.rs b/src/app/radarr.rs index e871c1f..72aa4d9 100644 --- a/src/app/radarr.rs +++ b/src/app/radarr.rs @@ -752,10 +752,18 @@ impl<'a> App<'a> { self.dispatch_by_radarr_block(&active_radarr_block).await; } - if self.is_routing || self.tick_count % self.tick_until_poll == 0 { + if self.is_routing { + if self.is_loading { + self.cancellation_token.cancel(); + } + self.dispatch_by_radarr_block(&active_radarr_block).await; self.refresh_metadata().await; } + + if self.tick_count % self.tick_until_poll == 0 { + self.refresh_metadata().await; + } } async fn refresh_metadata(&mut self) { diff --git a/src/app/radarr_tests.rs b/src/app/radarr_tests.rs index 1b433dc..dce036f 100644 --- a/src/app/radarr_tests.rs +++ b/src/app/radarr_tests.rs @@ -1115,17 +1115,6 @@ mod tests { assert!(!app.data.radarr_data.prompt_confirm); } - #[tokio::test] - async fn test_radarr_on_tick_not_routing() { - let mut app = App::default(); - - app - .radarr_on_tick(ActiveRadarrBlock::Downloads, false) - .await; - - assert!(!app.is_routing); - } - #[tokio::test] async fn test_radarr_on_tick_routing() { let (mut app, mut sync_network_rx) = construct_app_unit(); @@ -1159,6 +1148,41 @@ mod tests { assert!(!app.data.radarr_data.prompt_confirm); } + #[tokio::test] + async fn test_radarr_on_tick_routing_while_long_request_is_running_should_cancel_request() { + let (mut app, mut sync_network_rx) = construct_app_unit(); + app.is_routing = true; + app.is_loading = true; + + app + .radarr_on_tick(ActiveRadarrBlock::Downloads, false) + .await; + + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::GetDownloads.into() + ); + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::GetQualityProfiles.into() + ); + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::GetTags.into() + ); + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::GetRootFolders.into() + ); + assert_eq!( + sync_network_rx.recv().await.unwrap(), + RadarrEvent::GetDownloads.into() + ); + assert!(app.is_loading); + assert!(!app.data.radarr_data.prompt_confirm); + assert!(app.cancellation_token.is_cancelled()); + } + #[tokio::test] async fn test_radarr_on_tick_should_refresh() { let (mut app, mut sync_network_rx) = construct_app_unit(); @@ -1187,10 +1211,6 @@ mod tests { .radarr_on_tick(ActiveRadarrBlock::Downloads, false) .await; - assert_eq!( - sync_network_rx.recv().await.unwrap(), - RadarrEvent::GetDownloads.into() - ); assert_eq!( sync_network_rx.recv().await.unwrap(), RadarrEvent::GetQualityProfiles.into() @@ -1208,7 +1228,6 @@ mod tests { RadarrEvent::GetDownloads.into() ); assert!(app.is_loading); - assert!(!app.data.radarr_data.prompt_confirm); } #[tokio::test] diff --git a/src/main.rs b/src/main.rs index 19099cc..79deb1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use crossterm::terminal::{ }; use tokio::sync::mpsc::Receiver; use tokio::sync::{mpsc, Mutex}; +use tokio_util::sync::CancellationToken; use tui::backend::CrosstermBackend; use tui::Terminal; @@ -44,12 +45,17 @@ async fn main() -> Result<()> { let config = confy::load("managarr", "config")?; let (sync_network_tx, sync_network_rx) = mpsc::channel(500); + let cancellation_token = CancellationToken::new(); - let app = Arc::new(Mutex::new(App::new(sync_network_tx, config))); + let app = Arc::new(Mutex::new(App::new( + sync_network_tx, + config, + cancellation_token.clone(), + ))); let app_nw = Arc::clone(&app); - std::thread::spawn(move || start_networking(sync_network_rx, &app_nw)); + std::thread::spawn(move || start_networking(sync_network_rx, &app_nw, cancellation_token)); start_ui(&app).await?; @@ -57,8 +63,12 @@ async fn main() -> Result<()> { } #[tokio::main] -async fn start_networking(mut network_rx: Receiver, app: &Arc>>) { - let network = Network::new(app); +async fn start_networking( + mut network_rx: Receiver, + app: &Arc>>, + cancellation_token: CancellationToken, +) { + let mut network = Network::new(app, cancellation_token); while let Some(network_event) = network_rx.recv().await { network.handle_network_event(network_event).await; diff --git a/src/network/mod.rs b/src/network/mod.rs index 13e5e97..5832d3e 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -2,13 +2,15 @@ use std::fmt::Debug; use std::sync::Arc; use anyhow::anyhow; -use log::{debug, error}; +use log::{debug, error, warn}; use regex::Regex; use reqwest::{Client, RequestBuilder}; use serde::de::DeserializeOwned; use serde::Serialize; use strum_macros::Display; +use tokio::select; use tokio::sync::{Mutex, MutexGuard}; +use tokio_util::sync::CancellationToken; use crate::app::App; use crate::network::radarr_network::RadarrEvent; @@ -27,18 +29,20 @@ pub enum NetworkEvent { pub struct Network<'a, 'b> { client: Client, + cancellation_token: CancellationToken, pub app: &'a Arc>>, } impl<'a, 'b> Network<'a, 'b> { - pub fn new(app: &'a Arc>>) -> Self { + pub fn new(app: &'a Arc>>, cancellation_token: CancellationToken) -> Self { Network { client: Client::new(), app, + cancellation_token, } } - pub async fn handle_network_event(&self, network_event: NetworkEvent) { + pub async fn handle_network_event(&mut self, network_event: NetworkEvent) { match network_event { NetworkEvent::Radarr(radarr_event) => self.handle_radarr_event(radarr_event).await, } @@ -48,7 +52,7 @@ impl<'a, 'b> Network<'a, 'b> { } pub async fn handle_request( - &self, + &mut self, request_props: RequestProps, mut app_update_fn: impl FnMut(R, MutexGuard<'_, App<'_>>), ) where @@ -56,54 +60,65 @@ impl<'a, 'b> Network<'a, 'b> { R: DeserializeOwned, { let method = request_props.method; - match self.call_api(request_props).await.send().await { - Ok(response) => { - if response.status().is_success() { - match method { - RequestMethod::Get | RequestMethod::Post => { - match utils::parse_response::(response).await { - Ok(value) => { - let app = self.app.lock().await; - app_update_fn(value, app); - } - Err(e) => { - error!("Failed to parse response! {:?}", e); - self - .app - .lock() - .await - .handle_error(anyhow!("Failed to parse response! {:?}", e)); - } - } - } - RequestMethod::Delete | RequestMethod::Put => (), - } - } else { - let status = response.status(); - let whitespace_regex = Regex::new(r"\s+").unwrap(); - let response_body = response.text().await.unwrap_or_default(); - let error_body = whitespace_regex - .replace_all(&response_body.replace('\n', " "), " ") - .to_string(); - - error!( - "Request failed. Received {} response code with body: {}", - status, response_body - ); - self.app.lock().await.handle_error(anyhow!( - "Request failed. Received {} response code with body: {}", - status, - error_body - )); - } + let request_uri = request_props.uri.clone(); + select! { + _ = self.cancellation_token.cancelled() => { + warn!("Received Cancel request. Cancelling request to: {}", request_uri); + let mut app = self.app.lock().await; + self.cancellation_token = app.reset_cancellation_token(); + app.is_loading = false; } - Err(e) => { - error!("Failed to send request. {:?}", e); - self - .app - .lock() - .await - .handle_error(anyhow!("Failed to send request. {} ", e)); + resp = tokio::spawn(self.call_api(request_props).await.send()) => { + match resp.unwrap() { + Ok(response) => { + if response.status().is_success() { + match method { + RequestMethod::Get | RequestMethod::Post => { + match utils::parse_response::(response).await { + Ok(value) => { + let app = self.app.lock().await; + app_update_fn(value, app); + } + Err(e) => { + error!("Failed to parse response! {:?}", e); + self + .app + .lock() + .await + .handle_error(anyhow!("Failed to parse response! {:?}", e)); + } + } + } + RequestMethod::Delete | RequestMethod::Put => (), + } + } else { + let status = response.status(); + let whitespace_regex = Regex::new(r"\s+").unwrap(); + let response_body = response.text().await.unwrap_or_default(); + let error_body = whitespace_regex + .replace_all(&response_body.replace('\n', " "), " ") + .to_string(); + + error!( + "Request failed. Received {} response code with body: {}", + status, response_body + ); + self.app.lock().await.handle_error(anyhow!( + "Request failed. Received {} response code with body: {}", + status, + error_body + )); + } + } + Err(e) => { + error!("Failed to send request. {:?}", e); + self + .app + .lock() + .await + .handle_error(anyhow!("Failed to send request. {} ", e)); + } + } } } } diff --git a/src/network/network_tests.rs b/src/network/network_tests.rs index e4bc439..97ee5e1 100644 --- a/src/network/network_tests.rs +++ b/src/network/network_tests.rs @@ -8,12 +8,13 @@ mod tests { use pretty_assertions::assert_str_eq; use rstest::rstest; use serde::{Deserialize, Serialize}; - use tokio::sync::Mutex; + use tokio::sync::{mpsc, Mutex}; + use tokio_util::sync::CancellationToken; - use crate::app::{App, RadarrConfig}; + use crate::app::{App, AppConfig, RadarrConfig}; use crate::models::HorizontallyScrollableText; use crate::network::radarr_network::RadarrEvent; - use crate::network::{Network, RequestMethod, RequestProps}; + use crate::network::{Network, NetworkEvent, RequestMethod, RequestProps}; #[tokio::test] async fn test_handle_network_event_radarr_event() { @@ -38,7 +39,7 @@ mod tests { }; app.config.radarr = radarr_config; let app_arc = Arc::new(Mutex::new(app)); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_network_event(RadarrEvent::HealthCheck.into()) @@ -62,7 +63,7 @@ mod tests { .create_async() .await; let app_arc = Arc::new(Mutex::new(App::default())); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::( @@ -87,7 +88,7 @@ mod tests { #[values(RequestMethod::Get, RequestMethod::Post)] request_method: RequestMethod, ) { let (async_server, app_arc, server) = mock_api(request_method, 200, true).await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::<(), Test>( @@ -105,6 +106,37 @@ mod tests { assert_str_eq!(app_arc.lock().await.error.text, "Test"); } + #[tokio::test] + async fn test_handle_request_request_is_cancelled() { + let (async_server, _, server) = mock_api(RequestMethod::Get, 200, true).await; + let cancellation_token = CancellationToken::new(); + let (tx, _) = mpsc::channel::(500); + let app_arc = Arc::new(Mutex::new(App::new( + tx, + AppConfig::default(), + cancellation_token.clone(), + ))); + app_arc.lock().await.is_loading = true; + let mut network = Network::new(&app_arc, cancellation_token); + network.cancellation_token.cancel(); + + network + .handle_request::<(), Test>( + RequestProps { + uri: format!("{}/test", server.url()), + method: RequestMethod::Get, + body: None, + api_token: "test1234".to_owned(), + }, + |_, _| (), + ) + .await; + + assert!(!async_server.matched_async().await); + assert!(app_arc.lock().await.error.text.is_empty()); + assert!(!network.cancellation_token.is_cancelled()); + } + #[tokio::test] async fn test_handle_request_get_invalid_body() { let mut server = Server::new_async().await; @@ -116,7 +148,7 @@ mod tests { .create_async() .await; let app_arc = Arc::new(Mutex::new(App::default())); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::<(), Test>( @@ -142,7 +174,7 @@ mod tests { #[tokio::test] async fn test_handle_request_failure_to_send_request() { let app_arc = Arc::new(Mutex::new(App::default())); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::<(), Test>( @@ -176,7 +208,7 @@ mod tests { request_method: RequestMethod, ) { let (async_server, app_arc, server) = mock_api(request_method, 404, true).await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::<(), Test>( @@ -200,7 +232,7 @@ mod tests { #[tokio::test] async fn test_handle_request_non_success_code_empty_response_body() { let (async_server, app_arc, server) = mock_api(RequestMethod::Post, 404, false).await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_request::<(), Test>( @@ -252,7 +284,7 @@ mod tests { async_server = async_server.create_async().await; let app_arc = Arc::new(Mutex::new(App::default())); - let network = Network::new(&app_arc); + let network = Network::new(&app_arc, CancellationToken::new()); network .call_api(RequestProps { diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 9245558..b261ad8 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -107,7 +107,7 @@ impl From for NetworkEvent { } impl<'a, 'b> Network<'a, 'b> { - pub async fn handle_radarr_event(&self, radarr_event: RadarrEvent) { + pub async fn handle_radarr_event(&mut self, radarr_event: RadarrEvent) { match radarr_event { RadarrEvent::AddMovie => self.add_movie().await, RadarrEvent::AddRootFolder => self.add_root_folder().await, @@ -148,7 +148,7 @@ impl<'a, 'b> Network<'a, 'b> { } } - async fn add_movie(&self) { + async fn add_movie(&mut self) { info!("Adding new movie to Radarr"); let body = { let quality_profile_id = self.extract_quality_profile_id().await; @@ -226,7 +226,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn add_root_folder(&self) { + async fn add_root_folder(&mut self) { info!("Adding new root folder to Radarr"); let body = AddRootFolderBody { path: self.app.lock().await.data.radarr_data.edit_path.drain(), @@ -247,7 +247,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn add_tag(&self, tag: String) { + async fn add_tag(&mut self, tag: String) { info!("Adding a new Radarr tag"); let request_props = self @@ -269,7 +269,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn delete_download(&self) { + async fn delete_download(&mut self) { let download_id = self .app .lock() @@ -300,7 +300,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn delete_indexer(&self) { + async fn delete_indexer(&mut self) { let indexer_id = self .app .lock() @@ -331,7 +331,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn delete_movie(&self) { + async fn delete_movie(&mut self) { let movie_id = self.extract_movie_id().await; let delete_files = self.app.lock().await.data.radarr_data.delete_movie_files; let add_import_exclusion = self.app.lock().await.data.radarr_data.add_list_exclusion; @@ -369,7 +369,7 @@ impl<'a, 'b> Network<'a, 'b> { .reset_delete_movie_preferences(); } - async fn delete_root_folder(&self) { + async fn delete_root_folder(&mut self) { let root_folder_id = self .app .lock() @@ -405,7 +405,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn download_release(&self) { + async fn download_release(&mut self) { let (guid, title, indexer_id) = { let app = self.app.lock().await; let Release { @@ -435,7 +435,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn edit_collection(&self) { + async fn edit_collection(&mut self) { info!("Editing Radarr collection"); info!("Fetching collection details"); @@ -511,7 +511,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn edit_movie(&self) { + async fn edit_movie(&mut self) { info!("Editing Radarr movie"); info!("Fetching movie details"); @@ -573,7 +573,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_collections(&self) { + async fn get_collections(&mut self) { info!("Fetching Radarr collections"); let request_props = self @@ -591,18 +591,14 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_credits(&self) { + async fn get_credits(&mut self) { info!("Fetching Radarr movie credits"); + let request_uri = self + .append_movie_id_param(RadarrEvent::GetMovieCredits.resource()) + .await; let request_props = self - .radarr_request_props_from( - self - .append_movie_id_param(RadarrEvent::GetMovieCredits.resource()) - .await - .as_str(), - RequestMethod::Get, - None::<()>, - ) + .radarr_request_props_from(request_uri.as_str(), RequestMethod::Get, None::<()>) .await; self @@ -624,7 +620,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_diskspace(&self) { + async fn get_diskspace(&mut self) { info!("Fetching Radarr disk space"); let request_props = self @@ -642,7 +638,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_downloads(&self) { + async fn get_downloads(&mut self) { info!("Fetching Radarr downloads"); let request_props = self @@ -664,7 +660,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn get_indexers(&self) { + async fn get_indexers(&mut self) { info!("Fetching Radarr indexers"); let request_props = self @@ -682,7 +678,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn get_indexer_settings(&self) { + async fn get_indexer_settings(&mut self) { info!("Fetching Radarr indexer settings"); let request_props = self @@ -700,7 +696,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_healthcheck(&self) { + async fn get_healthcheck(&mut self) { info!("Performing Radarr health check"); let request_props = self @@ -716,7 +712,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_logs(&self) { + async fn get_logs(&mut self) { info!("Fetching Radarr logs"); let resource = format!( @@ -762,7 +758,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_movie_details(&self) { + async fn get_movie_details(&mut self) { info!("Fetching Radarr movie details"); let movie_id = self.extract_movie_id().await; @@ -918,18 +914,14 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_movie_history(&self) { + async fn get_movie_history(&mut self) { info!("Fetching Radarr movie history"); + let request_uri = self + .append_movie_id_param(RadarrEvent::GetMovieHistory.resource()) + .await; let request_props = self - .radarr_request_props_from( - self - .append_movie_id_param(RadarrEvent::GetMovieHistory.resource()) - .await - .as_str(), - RequestMethod::Get, - None::<()>, - ) + .radarr_request_props_from(request_uri.as_str(), RequestMethod::Get, None::<()>) .await; self @@ -945,7 +937,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_movies(&self) { + async fn get_movies(&mut self) { info!("Fetching Radarr library"); let request_props = self @@ -963,7 +955,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_quality_profiles(&self) { + async fn get_quality_profiles(&mut self) { info!("Fetching Radarr quality profiles"); let request_props = self @@ -984,7 +976,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_queued_events(&self) { + async fn get_queued_events(&mut self) { info!("Fetching Radarr queued events"); let request_props = self @@ -1006,7 +998,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_releases(&self) { + async fn get_releases(&mut self) { let movie_id = self.extract_movie_id().await; info!("Fetching releases for movie with id: {}", movie_id); @@ -1030,7 +1022,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_root_folders(&self) { + async fn get_root_folders(&mut self) { info!("Fetching Radarr root folders"); let request_props = self @@ -1048,7 +1040,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_status(&self) { + async fn get_status(&mut self) { info!("Fetching Radarr system status"); let request_props = self @@ -1067,7 +1059,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_tags(&self) { + async fn get_tags(&mut self) { info!("Fetching Radarr tags"); let request_props = self @@ -1088,7 +1080,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_tasks(&self) { + async fn get_tasks(&mut self) { info!("Fetching Radarr tasks"); let request_props = self @@ -1106,7 +1098,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn get_updates(&self) { + async fn get_updates(&mut self) { info!("Fetching Radarr updates"); let request_props = self @@ -1197,7 +1189,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn search_movie(&self) { + async fn search_movie(&mut self) { info!("Searching for specific Radarr movie"); let search_string = self.app.lock().await.data.radarr_data.search.text.clone(); @@ -1229,7 +1221,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn start_task(&self) { + async fn start_task(&mut self) { let task_name = self .app .lock() @@ -1258,7 +1250,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn trigger_automatic_search(&self) { + async fn trigger_automatic_search(&mut self) { let movie_id = self.extract_movie_id().await; info!("Searching indexers for movie with id: {}", movie_id); let body = MovieCommandBody { @@ -1279,7 +1271,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn update_all_movies(&self) { + async fn update_all_movies(&mut self) { info!("Updating all movies"); let body = MovieCommandBody { name: "RefreshMovie".to_owned(), @@ -1299,7 +1291,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn update_and_scan(&self) { + async fn update_and_scan(&mut self) { let movie_id = self.extract_movie_id().await; info!("Updating and scanning movie with id: {}", movie_id); let body = MovieCommandBody { @@ -1320,7 +1312,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn update_collections(&self) { + async fn update_collections(&mut self) { info!("Updating collections"); let body = CommandBody { name: "RefreshCollections".to_owned(), @@ -1339,7 +1331,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn update_downloads(&self) { + async fn update_downloads(&mut self) { info!("Updating downloads"); let body = CommandBody { name: "RefreshMonitoredDownloads".to_owned(), @@ -1358,7 +1350,7 @@ impl<'a, 'b> Network<'a, 'b> { .await; } - async fn update_indexer_settings(&self) { + async fn update_indexer_settings(&mut self) { info!("Updating Radarr indexer settings"); let body = self @@ -1416,7 +1408,7 @@ impl<'a, 'b> Network<'a, 'b> { } } - async fn extract_quality_profile_id(&self) -> u64 { + async fn extract_quality_profile_id(&mut self) -> u64 { let app = self.app.lock().await; let quality_profile = app .data @@ -1434,7 +1426,7 @@ impl<'a, 'b> Network<'a, 'b> { .unwrap() } - async fn extract_and_add_tag_ids_vec(&self) -> Vec { + async fn extract_and_add_tag_ids_vec(&mut self) -> Vec { let tags_map = self.app.lock().await.data.radarr_data.tags_map.clone(); let edit_tags = self.app.lock().await.data.radarr_data.edit_tags.drain(); let tags = edit_tags.clone(); @@ -1462,7 +1454,7 @@ impl<'a, 'b> Network<'a, 'b> { .collect() } - async fn extract_movie_id(&self) -> u64 { + async fn extract_movie_id(&mut self) -> u64 { if !self .app .lock() @@ -1499,7 +1491,7 @@ impl<'a, 'b> Network<'a, 'b> { } } - async fn extract_collection_id(&self) -> u64 { + async fn extract_collection_id(&mut self) -> u64 { if !self .app .lock() @@ -1536,7 +1528,7 @@ impl<'a, 'b> Network<'a, 'b> { } } - async fn append_movie_id_param(&self, resource: &str) -> String { + async fn append_movie_id_param(&mut self, resource: &str) -> String { let movie_id = self.extract_movie_id().await; format!("{}?movieId={}", resource, movie_id) } diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index 28724d3..fc19c1e 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -10,6 +10,7 @@ mod test { use serde_json::{json, Value}; use strum::IntoEnumIterator; use tokio::sync::Mutex; + use tokio_util::sync::CancellationToken; use crate::app::radarr::ActiveRadarrBlock; use crate::models::radarr_models::{ @@ -194,7 +195,7 @@ mod test { RadarrEvent::HealthCheck.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::HealthCheck).await; @@ -219,7 +220,7 @@ mod test { RadarrEvent::GetOverview.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetOverview).await; @@ -251,7 +252,7 @@ mod test { RadarrEvent::GetStatus.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetStatus).await; @@ -273,7 +274,7 @@ mod test { RadarrEvent::GetMovies.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetMovies).await; @@ -311,7 +312,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetReleases).await; @@ -357,7 +358,7 @@ mod test { ) .await; app_arc.lock().await.data.radarr_data.search = "test term".to_owned().into(); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::SearchNewMovie) @@ -397,7 +398,7 @@ mod test { task_name: "TestTask".to_owned(), ..Task::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::StartTask).await; @@ -413,7 +414,7 @@ mod test { let (async_server, app_arc, _server) = mock_radarr_api(RequestMethod::Get, None, Some(json!([])), &resource).await; app_arc.lock().await.data.radarr_data.search = "test term".to_owned().into(); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::SearchNewMovie) @@ -453,7 +454,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::TriggerAutomaticSearch) @@ -481,7 +482,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::UpdateAndScan) @@ -502,7 +503,7 @@ mod test { RadarrEvent::UpdateAllMovies.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::UpdateAllMovies) @@ -522,7 +523,7 @@ mod test { RadarrEvent::UpdateDownloads.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::UpdateDownloads) @@ -553,7 +554,7 @@ mod test { .await; app_arc.lock().await.data.radarr_data.indexer_settings = Some(indexer_settings()); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::UpdateIndexerSettings) @@ -580,7 +581,7 @@ mod test { RadarrEvent::UpdateCollections.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::UpdateCollections) @@ -608,7 +609,7 @@ mod test { .set_items(vec![movie()]); app_arc.lock().await.data.radarr_data.quality_profile_map = BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetMovieDetails) @@ -715,7 +716,7 @@ mod test { .set_items(vec![movie()]); app_arc.lock().await.data.radarr_data.quality_profile_map = BiMap::from_iter([(2222, "HD - 1080p".to_owned())]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetMovieDetails) @@ -795,7 +796,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetMovieHistory) @@ -846,7 +847,7 @@ mod test { RadarrEvent::GetCollections.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetCollections) @@ -881,7 +882,7 @@ mod test { RadarrEvent::GetDownloads.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetDownloads).await; @@ -946,7 +947,7 @@ mod test { RadarrEvent::GetIndexers.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetIndexers).await; @@ -977,7 +978,7 @@ mod test { RadarrEvent::GetIndexerSettings.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetIndexerSettings) @@ -1021,7 +1022,7 @@ mod test { RadarrEvent::GetQueuedEvents.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetQueuedEvents) @@ -1076,7 +1077,7 @@ mod test { &resource, ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetLogs).await; @@ -1109,7 +1110,7 @@ mod test { RadarrEvent::GetQualityProfiles.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetQualityProfiles) @@ -1135,7 +1136,7 @@ mod test { RadarrEvent::GetTags.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetTags).await; @@ -1190,7 +1191,7 @@ mod test { RadarrEvent::GetTasks.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetTasks).await; @@ -1279,7 +1280,7 @@ mod test { RadarrEvent::GetUpdates.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::GetUpdates).await; @@ -1301,7 +1302,7 @@ mod test { .await; app_arc.lock().await.data.radarr_data.tags_map = BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.add_tag("testing".to_owned()).await; @@ -1331,7 +1332,7 @@ mod test { RadarrEvent::GetRootFolders.resource(), ) .await; - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetRootFolders) @@ -1369,7 +1370,7 @@ mod test { .radarr_data .movies .set_items(vec![movie()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::GetMovieCredits) @@ -1400,7 +1401,7 @@ mod test { app.data.radarr_data.delete_movie_files = true; app.data.radarr_data.add_list_exclusion = true; } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::DeleteMovie).await; @@ -1421,7 +1422,7 @@ mod test { .radarr_data .downloads .set_items(vec![download_record()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::DeleteDownload) @@ -1442,7 +1443,7 @@ mod test { .radarr_data .indexers .set_items(vec![indexer()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::DeleteIndexer) @@ -1463,7 +1464,7 @@ mod test { .radarr_data .root_folders .set_items(vec![root_folder()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::DeleteRootFolder) @@ -1548,7 +1549,7 @@ mod test { .set_items(vec![add_movie_search_result()]); } } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::AddMovie).await; @@ -1568,7 +1569,7 @@ mod test { .await; app_arc.lock().await.data.radarr_data.edit_path = HorizontallyScrollableText::from("/nfs/test"); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::AddRootFolder) @@ -1636,7 +1637,7 @@ mod test { app.data.radarr_data.quality_profile_map = BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]); } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network.handle_radarr_event(RadarrEvent::EditMovie).await; @@ -1731,7 +1732,7 @@ mod test { app.data.radarr_data.quality_profile_map = BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]); } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::EditCollection) @@ -1765,7 +1766,7 @@ mod test { .radarr_data .movie_releases .set_items(vec![release()]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); network .handle_radarr_event(RadarrEvent::DownloadRelease) @@ -1787,7 +1788,7 @@ mod test { app.data.radarr_data.quality_profile_map = BiMap::from_iter([(1, "Any".to_owned()), (2, "HD - 1080p".to_owned())]); } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_quality_profile_id().await, 1); } @@ -1804,7 +1805,7 @@ mod test { (3, "hi".to_owned()), ]); } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_and_add_tag_ids_vec().await, vec![2, 3, 1]); } @@ -1824,7 +1825,7 @@ mod test { app.data.radarr_data.tags_map = BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]); } - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); let tag_ids_vec = network.extract_and_add_tag_ids_vec().await; @@ -1853,7 +1854,7 @@ mod test { id: Number::from(1), ..Movie::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_movie_id().await, 1); } @@ -1871,7 +1872,7 @@ mod test { id: Number::from(1), ..Movie::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_movie_id().await, 1); } @@ -1889,7 +1890,7 @@ mod test { id: Number::from(1), ..Collection::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_collection_id().await, 1); } @@ -1907,7 +1908,7 @@ mod test { id: Number::from(1), ..Collection::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_eq!(network.extract_collection_id().await, 1); } @@ -1925,7 +1926,7 @@ mod test { id: Number::from(1), ..Movie::default() }]); - let network = Network::new(&app_arc); + let mut network = Network::new(&app_arc, CancellationToken::new()); assert_str_eq!( network.append_movie_id_param("/test").await, @@ -1936,7 +1937,7 @@ mod test { #[tokio::test] async fn test_radarr_request_props_from_default_radarr_config() { let app_arc = Arc::new(Mutex::new(App::default())); - let network = Network::new(&app_arc); + let network = Network::new(&app_arc, CancellationToken::new()); let request_props = network .radarr_request_props_from("/test", RequestMethod::Get, None::<()>) @@ -1963,7 +1964,7 @@ mod test { port: Some(8080), api_token: api_token.clone(), }; - let network = Network::new(&app_arc); + let network = Network::new(&app_arc, CancellationToken::new()); let request_props = network .radarr_request_props_from("/test", RequestMethod::Get, None::<()>)