diff --git a/src/app/mod.rs b/src/app/mod.rs index 5d8153e..eba8dec 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -200,10 +200,14 @@ impl App<'_> { } pub fn get_current_route(&self) -> Route { - *self - .navigation_stack - .last() - .unwrap_or(&self.server_tabs.tabs.first().unwrap().route) + *self.navigation_stack.last().unwrap_or( + &self + .server_tabs + .tabs + .first() + .expect("At least one server tab must exist") + .route, + ) } } @@ -474,8 +478,8 @@ where fn interpolate_env_vars(s: &str) -> String { let result = s.to_string(); - let scrubbing_regex = Regex::new(r#"[\s\{\}!\$^\(\)\[\]\\\|`'"]+"#).unwrap(); - let var_regex = Regex::new(r"\$\{(.*?)\}").unwrap(); + let scrubbing_regex = Regex::new(r#"[\s{}!$^()\[\]\\|`'"]+"#).unwrap(); + let var_regex = Regex::new(r"\$\{(.*?)}").unwrap(); var_regex .replace_all(s, |caps: ®ex::Captures<'_>| { diff --git a/src/handlers/table_handler.rs b/src/handlers/table_handler.rs index e17fa25..77e32c4 100644 --- a/src/handlers/table_handler.rs +++ b/src/handlers/table_handler.rs @@ -56,12 +56,16 @@ macro_rules! handle_table_events { _ if $crate::matches_key!(submit, $self.key) => $self.[](config), _ if $crate::matches_key!(esc, $self.key) => $self.[](config), _ if config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table") => { $self.[]() } _ if config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table") => { $self.[]() } @@ -87,7 +91,9 @@ macro_rules! handle_table_events { true } _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { $table.sort.as_mut().unwrap().scroll_up(); true @@ -105,7 +111,9 @@ macro_rules! handle_table_events { true } _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { $table .sort @@ -151,7 +159,9 @@ macro_rules! handle_table_events { true } _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { $table .sort @@ -161,7 +171,9 @@ macro_rules! handle_table_events { true } _ if config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table") => { $table .search @@ -171,7 +183,9 @@ macro_rules! handle_table_events { true } _ if config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table") => { $table .filter @@ -193,7 +207,9 @@ macro_rules! handle_table_events { true } _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { $table .sort @@ -203,7 +219,9 @@ macro_rules! handle_table_events { true } _ if config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table") => { $table .search @@ -213,7 +231,9 @@ macro_rules! handle_table_events { true } _ if config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table") => { $table .filter @@ -229,7 +249,9 @@ macro_rules! handle_table_events { fn [](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { match $self.app.get_current_route() { _ if config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table") => { $crate::handle_text_box_left_right_keys!( $self, @@ -239,7 +261,9 @@ macro_rules! handle_table_events { true } _ if config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table") => { $crate::handle_text_box_left_right_keys!( $self, @@ -255,7 +279,9 @@ macro_rules! handle_table_events { fn [](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { match $self.app.get_current_route() { _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { if let Some(sort_by_fn) = config.sort_by_fn { $table.items.sort_by(sort_by_fn); @@ -267,7 +293,9 @@ macro_rules! handle_table_events { true } _ if config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table") => { $self.app.pop_navigation_stack(); $self.app.ignore_special_keys_for_textbox_input = false; @@ -290,7 +318,9 @@ macro_rules! handle_table_events { true } _ if config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table") => { $self.app.pop_navigation_stack(); $self.app.ignore_special_keys_for_textbox_input = false; @@ -319,15 +349,21 @@ macro_rules! handle_table_events { fn [](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool { match $self.app.get_current_route() { _ if config.sorting_block.is_some() - && $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() => + && $self.app.get_current_route() == *config.sorting_block + .as_ref() + .expect("sorting_block must be configured for this table") => { $self.app.pop_navigation_stack(); true } _ if (config.searching_block.is_some() - && $self.app.get_current_route() == *config.searching_block.as_ref().unwrap()) + && $self.app.get_current_route() == *config.searching_block + .as_ref() + .expect("searching_block must be configured for this table")) || (config.search_error_block.is_some() - && $self.app.get_current_route() == *config.search_error_block.as_ref().unwrap()) => + && $self.app.get_current_route() == *config.search_error_block + .as_ref() + .expect("search_error_block must be configured for this table")) => { $self.app.pop_navigation_stack(); $table.reset_search(); @@ -335,9 +371,13 @@ macro_rules! handle_table_events { true } _ if (config.filtering_block.is_some() - && $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap()) + && $self.app.get_current_route() == *config.filtering_block + .as_ref() + .expect("filtering_block must be configured for this table")) || (config.filter_error_block.is_some() - && $self.app.get_current_route() == *config.filter_error_block.as_ref().unwrap()) => + && $self.app.get_current_route() == *config.filter_error_block + .as_ref() + .expect("filter_error_block must be configured for this table")) => { $self.app.pop_navigation_stack(); $table.reset_filter(); diff --git a/src/models/servarr_data/radarr/modals.rs b/src/models/servarr_data/radarr/modals.rs index 55e3398..ca7b457 100644 --- a/src/models/servarr_data/radarr/modals.rs +++ b/src/models/servarr_data/radarr/modals.rs @@ -41,47 +41,53 @@ impl From<&RadarrData<'_>> for EditIndexerModal { } = radarr_data.indexers.current_selection(); let seed_ratio_field_option = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "seedCriteria.seedRatio"); + .find(|field| { + field.name.as_ref().expect("indexer field name must exist") == "seedCriteria.seedRatio" + }); let seed_ratio_value_option = if let Some(seed_ratio_field) = seed_ratio_field_option { seed_ratio_field.value.clone() } else { None }; - edit_indexer_modal.name = name.clone().unwrap().into(); + edit_indexer_modal.name = name.clone().expect("indexer name must exist").into(); edit_indexer_modal.enable_rss = Some(*enable_rss); edit_indexer_modal.enable_automatic_search = Some(*enable_automatic_search); edit_indexer_modal.enable_interactive_search = Some(*enable_interactive_search); edit_indexer_modal.priority = *priority; edit_indexer_modal.url = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "baseUrl") - .unwrap() + .find(|field| field.name.as_ref().expect("indexer field name must exist") == "baseUrl") + .expect("baseUrl field must exist") .value .clone() - .unwrap() + .expect("baseUrl field value must exist") .as_str() - .unwrap() + .expect("baseUrl field value must be a string") .into(); edit_indexer_modal.api_key = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "apiKey") - .unwrap() + .find(|field| field.name.as_ref().expect("indexer field name must exist") == "apiKey") + .expect("apiKey field must exist") .value .clone() - .unwrap() + .expect("apiKey field value must exist") .as_str() - .unwrap() + .expect("apiKey field value must be a string") .into(); if let Some(seed_ratio_value) = seed_ratio_value_option { - edit_indexer_modal.seed_ratio = seed_ratio_value.as_f64().unwrap().to_string().into(); + edit_indexer_modal.seed_ratio = seed_ratio_value + .as_f64() + .expect("Seed ratio value must be a valid f64") + .to_string() + .into(); } edit_indexer_modal.tags = tags @@ -89,8 +95,8 @@ impl From<&RadarrData<'_>> for EditIndexerModal { .map(|tag_id| { radarr_data .tags_map - .get_by_left(&tag_id.as_i64().unwrap()) - .unwrap() + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) + .expect("Tag ID must exist in tags map") .clone() }) .collect::>() @@ -131,8 +137,8 @@ impl From<&RadarrData<'_>> for EditMovieModal { .map(|tag_id| { radarr_data .tags_map - .get_by_left(&tag_id.as_i64().unwrap()) - .unwrap() + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) + .expect("Tag ID must exist in tags map") .clone() }) .collect::>() diff --git a/src/models/servarr_data/sonarr/modals.rs b/src/models/servarr_data/sonarr/modals.rs index c6e1e16..df7f8e4 100644 --- a/src/models/servarr_data/sonarr/modals.rs +++ b/src/models/servarr_data/sonarr/modals.rs @@ -86,47 +86,53 @@ impl From<&SonarrData<'_>> for EditIndexerModal { } = sonarr_data.indexers.current_selection(); let seed_ratio_field_option = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "seedCriteria.seedRatio"); + .find(|field| { + field.name.as_ref().expect("indexer field name must exist") == "seedCriteria.seedRatio" + }); let seed_ratio_value_option = if let Some(seed_ratio_field) = seed_ratio_field_option { seed_ratio_field.value.clone() } else { None }; - edit_indexer_modal.name = name.clone().unwrap().into(); + edit_indexer_modal.name = name.clone().expect("indexer name must exist").into(); edit_indexer_modal.enable_rss = Some(*enable_rss); edit_indexer_modal.enable_automatic_search = Some(*enable_automatic_search); edit_indexer_modal.enable_interactive_search = Some(*enable_interactive_search); edit_indexer_modal.priority = *priority; edit_indexer_modal.url = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "baseUrl") - .unwrap() + .find(|field| field.name.as_ref().expect("indexer field name must exist") == "baseUrl") + .expect("baseUrl field must exist") .value .clone() - .unwrap() + .expect("baseUrl field value must exist") .as_str() - .unwrap() + .expect("baseUrl field value must be a string") .into(); edit_indexer_modal.api_key = fields .as_ref() - .unwrap() + .expect("indexer fields must exist") .iter() - .find(|field| field.name.as_ref().unwrap() == "apiKey") - .unwrap() + .find(|field| field.name.as_ref().expect("indexer field name must exist") == "apiKey") + .expect("apiKey field must exist") .value .clone() - .unwrap() + .expect("apiKey field value must exist") .as_str() - .unwrap() + .expect("apiKey field value must be a string") .into(); if let Some(seed_ratio_value) = seed_ratio_value_option { - edit_indexer_modal.seed_ratio = seed_ratio_value.as_f64().unwrap().to_string().into(); + edit_indexer_modal.seed_ratio = seed_ratio_value + .as_f64() + .expect("Seed ratio value must be a valid f64") + .to_string() + .into(); } edit_indexer_modal.tags = tags @@ -134,8 +140,8 @@ impl From<&SonarrData<'_>> for EditIndexerModal { .map(|tag_id| { sonarr_data .tags_map - .get_by_left(&tag_id.as_i64().unwrap()) - .unwrap() + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) + .expect("Tag ID must exist in tags map") .clone() }) .collect::>() @@ -180,8 +186,8 @@ impl From<&SonarrData<'_>> for EditSeriesModal { .map(|tag_id| { sonarr_data .tags_map - .get_by_left(&tag_id.as_i64().unwrap()) - .unwrap() + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) + .expect("Tag ID must exist in tags map") .clone() }) .collect::>() diff --git a/src/models/stateful_table.rs b/src/models/stateful_table.rs index 3db5c61..a38512a 100644 --- a/src/models/stateful_table.rs +++ b/src/models/stateful_table.rs @@ -55,15 +55,32 @@ where return; } - match self.filtered_state.as_ref().unwrap().selected() { + match self + .filtered_state + .as_ref() + .expect("filtered_state must exist when filtered_items exists") + .selected() + { Some(i) => { if i >= filtered_items.len() - 1 { - self.filtered_state.as_mut().unwrap().select_first(); + self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_first(); } else { - self.filtered_state.as_mut().unwrap().select_next(); + self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_next(); } } - None => self.filtered_state.as_mut().unwrap().select_first(), + None => self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_first(), }; return; @@ -91,19 +108,32 @@ where return; } - match self.filtered_state.as_ref().unwrap().selected() { + match self + .filtered_state + .as_ref() + .expect("filtered_state must exist when filtered_items exists") + .selected() + { Some(i) => { if i == 0 { self .filtered_state .as_mut() - .unwrap() + .expect("filtered_state must exist when filtered_items exists") .select(Some(filtered_items.len() - 1)); } else { - self.filtered_state.as_mut().unwrap().select_previous(); + self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_previous(); } } - None => self.filtered_state.as_mut().unwrap().select_first(), + None => self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_first(), }; return; @@ -131,7 +161,11 @@ where return; } - self.filtered_state.as_mut().unwrap().select_first(); + self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_first(); return; } @@ -151,7 +185,7 @@ where self .filtered_state .as_mut() - .unwrap() + .expect("filtered_state must exist when filtered_items exists") .select(Some(filtered_items.len() - 1)); return; } @@ -174,15 +208,24 @@ where return; } - match self.filtered_state.as_ref().unwrap().selected() { + match self + .filtered_state + .as_ref() + .expect("filtered_state must exist when filtered_items exists") + .selected() + { Some(i) => { self .filtered_state .as_mut() - .unwrap() + .expect("filtered_state must exist when filtered_items exists") .select(Some(i.saturating_add(20) % (filtered_items.len() - 1))); } - None => self.filtered_state.as_mut().unwrap().select_first(), + None => self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_first(), }; return; @@ -208,16 +251,25 @@ where return; } - match self.filtered_state.as_ref().unwrap().selected() { + match self + .filtered_state + .as_ref() + .expect("filtered_state must exist when filtered_items exists") + .selected() + { Some(i) => { let len = filtered_items.len() - 1; self .filtered_state .as_mut() - .unwrap() + .expect("filtered_state must exist when filtered_items exists") .select(Some((i + len - (20 % len)) % len)); } - None => self.filtered_state.as_mut().unwrap().select_last(), + None => self + .filtered_state + .as_mut() + .expect("filtered_state must exist when filtered_items exists") + .select_last(), }; return; @@ -278,7 +330,7 @@ where &filtered_items[self .filtered_state .as_ref() - .unwrap() + .expect("filtered_state must exist when filtered_items exists") .selected() .unwrap_or(0)] } else { diff --git a/src/network/radarr_network/indexers/mod.rs b/src/network/radarr_network/indexers/mod.rs index e190ea7..e9c67ee 100644 --- a/src/network/radarr_network/indexers/mod.rs +++ b/src/network/radarr_network/indexers/mod.rs @@ -4,7 +4,7 @@ use crate::models::servarr_models::{EditIndexerParams, Indexer, IndexerTestResul use crate::models::stateful_table::StatefulTable; use crate::network::radarr_network::RadarrEvent; use crate::network::{Network, RequestMethod}; -use anyhow::Result; +use anyhow::{Context, Result}; use log::{debug, info}; use serde_json::{Value, json}; @@ -103,57 +103,57 @@ impl Network<'_, '_> { ) = { let priority = detailed_indexer_body["priority"] .as_i64() - .expect("Unable to deserialize 'priority'"); + .context("Failed to deserialize indexer 'priority' field")?; let seed_ratio_field_option = detailed_indexer_body["fields"] .as_array() - .unwrap() + .context("Failed to get indexer 'fields' array")? .iter() .find(|field| field["name"] == "seedCriteria.seedRatio"); let name = edit_indexer_params.name.unwrap_or( detailed_indexer_body["name"] .as_str() - .expect("Unable to deserialize 'name'") + .context("Failed to deserialize indexer 'name' field")? .to_owned(), ); let enable_rss = edit_indexer_params.enable_rss.unwrap_or( detailed_indexer_body["enableRss"] .as_bool() - .expect("Unable to deserialize 'enableRss'"), + .context("Failed to deserialize indexer 'enableRss' field")?, ); let enable_automatic_search = edit_indexer_params.enable_automatic_search.unwrap_or( detailed_indexer_body["enableAutomaticSearch"] .as_bool() - .expect("Unable to deserialize 'enableAutomaticSearch"), + .context("Failed to deserialize indexer 'enableAutomaticSearch' field")?, ); let enable_interactive_search = edit_indexer_params.enable_interactive_search.unwrap_or( detailed_indexer_body["enableInteractiveSearch"] .as_bool() - .expect("Unable to deserialize 'enableInteractiveSearch'"), + .context("Failed to deserialize indexer 'enableInteractiveSearch' field")?, ); let url = edit_indexer_params.url.unwrap_or( detailed_indexer_body["fields"] .as_array() - .expect("Unable to deserialize 'fields'") + .context("Failed to get indexer 'fields' array for baseUrl")? .iter() .find(|field| field["name"] == "baseUrl") - .expect("Field 'baseUrl' was not found in the 'fields' array") + .context("Field 'baseUrl' was not found in the indexer fields array")? .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'baseUrl value'") + .context("Failed to deserialize indexer 'baseUrl' value")? .to_owned(), ); let api_key = edit_indexer_params.api_key.unwrap_or( detailed_indexer_body["fields"] .as_array() - .expect("Unable to deserialize 'fields'") + .context("Failed to get indexer 'fields' array for apiKey")? .iter() .find(|field| field["name"] == "apiKey") - .expect("Field 'apiKey' was not found in the 'fields' array") + .context("Field 'apiKey' was not found in the indexer fields array")? .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'apiKey value'") + .context("Failed to deserialize indexer 'apiKey' value")? .to_owned(), ); let seed_ratio = edit_indexer_params.seed_ratio.unwrap_or_else(|| { @@ -162,7 +162,7 @@ impl Network<'_, '_> { .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'seedCriteria.seedRatio value'") + .unwrap_or("") .to_owned(); } @@ -174,10 +174,14 @@ impl Network<'_, '_> { edit_indexer_params.tags.unwrap_or( detailed_indexer_body["tags"] .as_array() - .expect("Unable to deserialize 'tags'") + .context("Failed to get indexer 'tags' array")? .iter() - .map(|item| item.as_i64().expect("Unable to deserialize tag ID")) - .collect(), + .map(|item| { + item + .as_i64() + .context("Failed to deserialize indexer tag ID") + }) + .collect::>>()?, ) }; let priority = edit_indexer_params.priority.unwrap_or(priority); @@ -195,47 +199,54 @@ impl Network<'_, '_> { ) }; - *detailed_indexer_body.get_mut("name").unwrap() = json!(name); - *detailed_indexer_body.get_mut("priority").unwrap() = json!(priority); - *detailed_indexer_body.get_mut("enableRss").unwrap() = json!(enable_rss); + *detailed_indexer_body + .get_mut("name") + .context("Failed to get mutable reference to indexer 'name' field")? = json!(name); + *detailed_indexer_body + .get_mut("priority") + .context("Failed to get mutable reference to indexer 'priority' field")? = json!(priority); + *detailed_indexer_body + .get_mut("enableRss") + .context("Failed to get mutable reference to indexer 'enableRss' field")? = json!(enable_rss); *detailed_indexer_body .get_mut("enableAutomaticSearch") - .unwrap() = json!(enable_automatic_search); + .context("Failed to get mutable reference to indexer 'enableAutomaticSearch' field")? = + json!(enable_automatic_search); *detailed_indexer_body .get_mut("enableInteractiveSearch") - .unwrap() = json!(enable_interactive_search); + .context("Failed to get mutable reference to indexer 'enableInteractiveSearch' field")? = + json!(enable_interactive_search); *detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array")? .iter_mut() .find(|field| field["name"] == "baseUrl") - .unwrap() + .context("Failed to find 'baseUrl' field in indexer fields array")? .get_mut("value") - .unwrap() = json!(url); + .context("Failed to get mutable reference to 'baseUrl' value")? = json!(url); *detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array for apiKey")? .iter_mut() .find(|field| field["name"] == "apiKey") - .unwrap() + .context("Failed to find 'apiKey' field in indexer fields array")? .get_mut("value") - .unwrap() = json!(api_key); - *detailed_indexer_body.get_mut("tags").unwrap() = json!(tags); + .context("Failed to get mutable reference to 'apiKey' value")? = json!(api_key); + *detailed_indexer_body + .get_mut("tags") + .context("Failed to get mutable reference to indexer 'tags' field")? = json!(tags); let seed_ratio_field_option = detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array for seed ratio")? .iter_mut() .find(|field| field["name"] == "seedCriteria.seedRatio"); if let Some(seed_ratio_field) = seed_ratio_field_option { seed_ratio_field .as_object_mut() - .unwrap() + .context("Failed to get mutable reference to 'seedCriteria.seedRatio' object")? .insert("value".to_string(), json!(seed_ratio)); } @@ -332,12 +343,13 @@ impl Network<'_, '_> { self .handle_request::(request_props, |test_results, mut app| { if test_results.as_object().is_none() { - app.data.radarr_data.indexer_test_errors = Some( - test_results.as_array().unwrap()[0] - .get("errorMessage") - .unwrap() - .to_string(), - ); + let error_message = test_results + .as_array() + .and_then(|arr| arr.first()) + .and_then(|item| item.get("errorMessage")) + .map(|msg| msg.to_string()) + .unwrap_or_else(|| "Unknown indexer test error".to_string()); + app.data.radarr_data.indexer_test_errors = Some(error_message); } else { app.data.radarr_data.indexer_test_errors = Some(String::new()); }; diff --git a/src/network/radarr_network/system/mod.rs b/src/network/radarr_network/system/mod.rs index 1dde56e..16383dd 100644 --- a/src/network/radarr_network/system/mod.rs +++ b/src/network/radarr_network/system/mod.rs @@ -72,17 +72,29 @@ impl Network<'_, '_> { "{}|{}|{}|{}|{}", log.time, log.level.to_uppercase(), - log.logger.as_ref().unwrap(), - log.exception_type.as_ref().unwrap(), - log.exception.as_ref().unwrap() + log + .logger + .as_ref() + .expect("logger must exist when exception is present"), + log + .exception_type + .as_ref() + .expect("exception_type must exist when exception is present"), + log + .exception + .as_ref() + .expect("exception must exist in this branch") )) } else { HorizontallyScrollableText::from(format!( "{}|{}|{}|{}", log.time, log.level.to_uppercase(), - log.logger.as_ref().unwrap(), - log.message.as_ref().unwrap() + log.logger.as_ref().expect("logger must exist in log entry"), + log + .message + .as_ref() + .expect("message must exist when exception is not present") )) } }) diff --git a/src/network/sonarr_network/indexers/mod.rs b/src/network/sonarr_network/indexers/mod.rs index 1fe256b..7200d47 100644 --- a/src/network/sonarr_network/indexers/mod.rs +++ b/src/network/sonarr_network/indexers/mod.rs @@ -4,7 +4,7 @@ use crate::models::sonarr_models::IndexerSettings; use crate::models::stateful_table::StatefulTable; use crate::network::sonarr_network::SonarrEvent; use crate::network::{Network, RequestMethod}; -use anyhow::Result; +use anyhow::{Context, Result}; use log::{debug, info}; use serde_json::{Value, json}; @@ -101,57 +101,57 @@ impl Network<'_, '_> { ) = { let priority = detailed_indexer_body["priority"] .as_i64() - .expect("Unable to deserialize 'priority'"); + .context("Failed to deserialize indexer 'priority' field")?; let seed_ratio_field_option = detailed_indexer_body["fields"] .as_array() - .unwrap() + .context("Failed to get indexer 'fields' array")? .iter() .find(|field| field["name"] == "seedCriteria.seedRatio"); let name = edit_indexer_params.name.unwrap_or( detailed_indexer_body["name"] .as_str() - .expect("Unable to deserialize 'name'") + .context("Failed to deserialize indexer 'name' field")? .to_owned(), ); let enable_rss = edit_indexer_params.enable_rss.unwrap_or( detailed_indexer_body["enableRss"] .as_bool() - .expect("Unable to deserialize 'enableRss'"), + .context("Failed to deserialize indexer 'enableRss' field")?, ); let enable_automatic_search = edit_indexer_params.enable_automatic_search.unwrap_or( detailed_indexer_body["enableAutomaticSearch"] .as_bool() - .expect("Unable to deserialize 'enableAutomaticSearch"), + .context("Failed to deserialize indexer 'enableAutomaticSearch' field")?, ); let enable_interactive_search = edit_indexer_params.enable_interactive_search.unwrap_or( detailed_indexer_body["enableInteractiveSearch"] .as_bool() - .expect("Unable to deserialize 'enableInteractiveSearch'"), + .context("Failed to deserialize indexer 'enableInteractiveSearch' field")?, ); let url = edit_indexer_params.url.unwrap_or( detailed_indexer_body["fields"] .as_array() - .expect("Unable to deserialize 'fields'") + .context("Failed to get indexer 'fields' array for baseUrl")? .iter() .find(|field| field["name"] == "baseUrl") - .expect("Field 'baseUrl' was not found in the 'fields' array") + .context("Field 'baseUrl' was not found in the indexer fields array")? .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'baseUrl value'") + .context("Failed to deserialize indexer 'baseUrl' value")? .to_owned(), ); let api_key = edit_indexer_params.api_key.unwrap_or( detailed_indexer_body["fields"] .as_array() - .expect("Unable to deserialize 'fields'") + .context("Failed to get indexer 'fields' array for apiKey")? .iter() .find(|field| field["name"] == "apiKey") - .expect("Field 'apiKey' was not found in the 'fields' array") + .context("Field 'apiKey' was not found in the indexer fields array")? .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'apiKey value'") + .context("Failed to deserialize indexer 'apiKey' value")? .to_owned(), ); let seed_ratio = edit_indexer_params.seed_ratio.unwrap_or_else(|| { @@ -160,7 +160,7 @@ impl Network<'_, '_> { .get("value") .unwrap_or(&json!("")) .as_str() - .expect("Unable to deserialize 'seedCriteria.seedRatio value'") + .unwrap_or("") .to_owned(); } @@ -172,10 +172,14 @@ impl Network<'_, '_> { edit_indexer_params.tags.unwrap_or( detailed_indexer_body["tags"] .as_array() - .expect("Unable to deserialize 'tags'") + .context("Failed to get indexer 'tags' array")? .iter() - .map(|item| item.as_i64().expect("Unable to deserialize tag ID")) - .collect(), + .map(|item| { + item + .as_i64() + .context("Failed to deserialize indexer tag ID") + }) + .collect::>>()?, ) }; let priority = edit_indexer_params.priority.unwrap_or(priority); @@ -193,47 +197,54 @@ impl Network<'_, '_> { ) }; - *detailed_indexer_body.get_mut("name").unwrap() = json!(name); - *detailed_indexer_body.get_mut("priority").unwrap() = json!(priority); - *detailed_indexer_body.get_mut("enableRss").unwrap() = json!(enable_rss); + *detailed_indexer_body + .get_mut("name") + .context("Failed to get mutable reference to indexer 'name' field")? = json!(name); + *detailed_indexer_body + .get_mut("priority") + .context("Failed to get mutable reference to indexer 'priority' field")? = json!(priority); + *detailed_indexer_body + .get_mut("enableRss") + .context("Failed to get mutable reference to indexer 'enableRss' field")? = json!(enable_rss); *detailed_indexer_body .get_mut("enableAutomaticSearch") - .unwrap() = json!(enable_automatic_search); + .context("Failed to get mutable reference to indexer 'enableAutomaticSearch' field")? = + json!(enable_automatic_search); *detailed_indexer_body .get_mut("enableInteractiveSearch") - .unwrap() = json!(enable_interactive_search); + .context("Failed to get mutable reference to indexer 'enableInteractiveSearch' field")? = + json!(enable_interactive_search); *detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array")? .iter_mut() .find(|field| field["name"] == "baseUrl") - .unwrap() + .context("Failed to find 'baseUrl' field in indexer fields array")? .get_mut("value") - .unwrap() = json!(url); + .context("Failed to get mutable reference to 'baseUrl' value")? = json!(url); *detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array for apiKey")? .iter_mut() .find(|field| field["name"] == "apiKey") - .unwrap() + .context("Failed to find 'apiKey' field in indexer fields array")? .get_mut("value") - .unwrap() = json!(api_key); - *detailed_indexer_body.get_mut("tags").unwrap() = json!(tags); + .context("Failed to get mutable reference to 'apiKey' value")? = json!(api_key); + *detailed_indexer_body + .get_mut("tags") + .context("Failed to get mutable reference to indexer 'tags' field")? = json!(tags); let seed_ratio_field_option = detailed_indexer_body .get_mut("fields") - .unwrap() - .as_array_mut() - .unwrap() + .and_then(|f| f.as_array_mut()) + .context("Failed to get mutable reference to indexer 'fields' array for seed ratio")? .iter_mut() .find(|field| field["name"] == "seedCriteria.seedRatio"); if let Some(seed_ratio_field) = seed_ratio_field_option { seed_ratio_field .as_object_mut() - .unwrap() + .context("Failed to get mutable reference to 'seedCriteria.seedRatio' object")? .insert("value".to_string(), json!(seed_ratio)); } @@ -330,12 +341,13 @@ impl Network<'_, '_> { self .handle_request::(request_props, |test_results, mut app| { if test_results.as_object().is_none() { - app.data.sonarr_data.indexer_test_errors = Some( - test_results.as_array().unwrap()[0] - .get("errorMessage") - .unwrap() - .to_string(), - ); + let error_message = test_results + .as_array() + .and_then(|arr| arr.first()) + .and_then(|item| item.get("errorMessage")) + .map(|msg| msg.to_string()) + .unwrap_or_else(|| "Unknown indexer test error".to_string()); + app.data.sonarr_data.indexer_test_errors = Some(error_message); } else { app.data.sonarr_data.indexer_test_errors = Some(String::new()); }; diff --git a/src/network/sonarr_network/system/mod.rs b/src/network/sonarr_network/system/mod.rs index 63798f2..9bd6b88 100644 --- a/src/network/sonarr_network/system/mod.rs +++ b/src/network/sonarr_network/system/mod.rs @@ -55,17 +55,29 @@ impl Network<'_, '_> { "{}|{}|{}|{}|{}", log.time, log.level.to_uppercase(), - log.logger.as_ref().unwrap(), - log.exception_type.as_ref().unwrap(), - log.exception.as_ref().unwrap() + log + .logger + .as_ref() + .expect("logger must exist when exception is present"), + log + .exception_type + .as_ref() + .expect("exception_type must exist when exception is present"), + log + .exception + .as_ref() + .expect("exception must exist in this branch") )) } else { HorizontallyScrollableText::from(format!( "{}|{}|{}|{}", log.time, log.level.to_uppercase(), - log.logger.as_ref().unwrap(), - log.message.as_ref().unwrap() + log.logger.as_ref().expect("logger must exist in log entry"), + log + .message + .as_ref() + .expect("message must exist when exception is not present") )) } }) diff --git a/src/ui/radarr_ui/library/add_movie_ui.rs b/src/ui/radarr_ui/library/add_movie_ui.rs index 8836d82..a046bc5 100644 --- a/src/ui/radarr_ui/library/add_movie_ui.rs +++ b/src/ui/radarr_ui/library/add_movie_ui.rs @@ -80,13 +80,19 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { Layout::vertical([Constraint::Length(3), Constraint::Fill(0)]) .margin(1) .areas(area); - let block_content = &app.data.radarr_data.add_movie_search.as_ref().unwrap().text; + let block_content = &app + .data + .radarr_data + .add_movie_search + .as_ref() + .expect("add_movie_search must be populated") + .text; let offset = app .data .radarr_data .add_movie_search .as_ref() - .unwrap() + .expect("add_movie_search must be populated") .offset .load(Ordering::SeqCst); let search_results_row_mapping = |movie: &AddMovieSearchResult| { @@ -260,7 +266,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .radarr_data .add_searched_movies .as_ref() - .unwrap() + .expect("add_searched_movies must be populated") .current_selection() .title .text, @@ -269,7 +275,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .radarr_data .add_searched_movies .as_ref() - .unwrap() + .expect("add_searched_movies must be populated") .current_selection() .overview .clone(), @@ -287,7 +293,12 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { root_folder_list, tags, .. - } = app.data.radarr_data.add_movie_modal.as_ref().unwrap(); + } = app + .data + .radarr_data + .add_movie_modal + .as_ref() + .expect("add_movie_modal must exist in this context"); let selected_monitor = monitor_list.current_selection(); let selected_minimum_availability = minimum_availability_list.current_selection(); @@ -378,7 +389,7 @@ fn draw_add_movie_select_monitor_popup(f: &mut Frame<'_>, app: &mut App<'_>) { .radarr_data .add_movie_modal .as_mut() - .unwrap() + .expect("add_movie_modal must exist in this context") .monitor_list, |monitor| ListItem::new(monitor.to_display_str().to_owned()), ); @@ -394,7 +405,7 @@ fn draw_add_movie_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mut .radarr_data .add_movie_modal .as_mut() - .unwrap() + .expect("add_movie_modal must exist in this context") .minimum_availability_list, |minimum_availability| ListItem::new(minimum_availability.to_display_str().to_owned()), ); @@ -410,7 +421,7 @@ fn draw_add_movie_select_quality_profile_popup(f: &mut Frame<'_>, app: &mut App< .radarr_data .add_movie_modal .as_mut() - .unwrap() + .expect("add_movie_modal must exist in this context") .quality_profile_list, |quality_profile| ListItem::new(quality_profile.clone()), ); @@ -426,7 +437,7 @@ fn draw_add_movie_select_root_folder_popup(f: &mut Frame<'_>, app: &mut App<'_>) .radarr_data .add_movie_modal .as_mut() - .unwrap() + .expect("add_movie_modal must exist in this context") .root_folder_list, |root_folder| ListItem::new(root_folder.path.to_owned()), ); diff --git a/src/ui/radarr_ui/library/edit_movie_ui.rs b/src/ui/radarr_ui/library/edit_movie_ui.rs index 46512d4..f944e81 100644 --- a/src/ui/radarr_ui/library/edit_movie_ui.rs +++ b/src/ui/radarr_ui/library/edit_movie_ui.rs @@ -87,7 +87,12 @@ fn draw_edit_movie_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, are monitored, path, tags, - } = app.data.radarr_data.edit_movie_modal.as_ref().unwrap(); + } = app + .data + .radarr_data + .edit_movie_modal + .as_ref() + .expect("edit_movie_modal must exist in this context"); let selected_minimum_availability = minimum_availability_list.current_selection(); let selected_quality_profile = quality_profile_list.current_selection(); @@ -175,7 +180,7 @@ fn draw_edit_movie_select_minimum_availability_popup(f: &mut Frame<'_>, app: &mu .radarr_data .edit_movie_modal .as_mut() - .unwrap() + .expect("edit_movie_modal must exist in this context") .minimum_availability_list, |minimum_availability| ListItem::new(minimum_availability.to_display_str().to_owned()), ); @@ -191,7 +196,7 @@ fn draw_edit_movie_select_quality_profile_popup(f: &mut Frame<'_>, app: &mut App .radarr_data .edit_movie_modal .as_mut() - .unwrap() + .expect("edit_movie_modal must exist in this context") .quality_profile_list, |quality_profile| ListItem::new(quality_profile.clone()), ); diff --git a/src/ui/radarr_ui/library/mod.rs b/src/ui/radarr_ui/library/mod.rs index 811851e..7e86def 100644 --- a/src/ui/radarr_ui/library/mod.rs +++ b/src/ui/radarr_ui/library/mod.rs @@ -99,7 +99,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let certification = movie.certification.clone().unwrap_or_default(); let quality_profile = quality_profile_map .get_by_left(&movie.quality_profile_id) - .unwrap() + .expect("Quality profile ID must exist in quality_profile_map") .to_owned(); let empty_tag = String::new(); let tags = if !movie.tags.is_empty() { @@ -108,7 +108,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .iter() .map(|tag_id| { tags_map - .get_by_left(&tag_id.as_i64().unwrap()) + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) .unwrap_or(&empty_tag) .clone() }) diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index fddc871..e479342 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -472,7 +472,7 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) { .radarr_data .movie_details_modal .as_ref() - .unwrap() + .expect("movie_details_modal must exist in this context") .movie_releases .current_selection(); let title = if current_selection.rejected { diff --git a/src/ui/sonarr_ui/library/add_series_ui.rs b/src/ui/sonarr_ui/library/add_series_ui.rs index 477f5a2..005fac1 100644 --- a/src/ui/sonarr_ui/library/add_series_ui.rs +++ b/src/ui/sonarr_ui/library/add_series_ui.rs @@ -80,14 +80,14 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .add_series_search .as_ref() - .unwrap() + .expect("add_series_search must be populated") .text; let offset = app .data .sonarr_data .add_series_search .as_ref() - .unwrap() + .expect("add_series_search must be populated") .offset .load(Ordering::SeqCst); let search_results_row_mapping = |series: &AddSeriesSearchResult| { @@ -232,7 +232,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .add_searched_series .as_ref() - .unwrap() + .expect("add_searched_series must be populated") .current_selection() .title .text, @@ -241,7 +241,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .add_searched_series .as_ref() - .unwrap() + .expect("add_searched_series must be populated") .current_selection() .overview .clone() @@ -261,7 +261,12 @@ fn draw_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { use_season_folder, tags, .. - } = app.data.sonarr_data.add_series_modal.as_ref().unwrap(); + } = app + .data + .sonarr_data + .add_series_modal + .as_ref() + .expect("add_series_modal must exist in this context"); let selected_monitor = monitor_list.current_selection(); let selected_series_type = series_type_list.current_selection(); @@ -367,7 +372,7 @@ fn draw_add_series_select_monitor_popup(f: &mut Frame<'_>, app: &mut App<'_>) { .sonarr_data .add_series_modal .as_mut() - .unwrap() + .expect("add_series_modal must exist in this context") .monitor_list, |monitor| ListItem::new(monitor.to_display_str().to_owned()), ); @@ -383,7 +388,7 @@ fn draw_add_series_select_series_type_popup(f: &mut Frame<'_>, app: &mut App<'_> .sonarr_data .add_series_modal .as_mut() - .unwrap() + .expect("add_series_modal must exist in this context") .series_type_list, |series_type| ListItem::new(series_type.to_display_str().to_owned()), ); @@ -399,7 +404,7 @@ fn draw_add_series_select_quality_profile_popup(f: &mut Frame<'_>, app: &mut App .sonarr_data .add_series_modal .as_mut() - .unwrap() + .expect("add_series_modal must exist in this context") .quality_profile_list, |quality_profile| ListItem::new(quality_profile.clone()), ); @@ -415,7 +420,7 @@ fn draw_add_series_select_language_profile_popup(f: &mut Frame<'_>, app: &mut Ap .sonarr_data .add_series_modal .as_mut() - .unwrap() + .expect("add_series_modal must exist in this context") .language_profile_list, |language_profile| ListItem::new(language_profile.clone()), ); @@ -431,7 +436,7 @@ fn draw_add_series_select_root_folder_popup(f: &mut Frame<'_>, app: &mut App<'_> .sonarr_data .add_series_modal .as_mut() - .unwrap() + .expect("add_series_modal must exist in this context") .root_folder_list, |root_folder| ListItem::new(root_folder.path.to_owned()), ); diff --git a/src/ui/sonarr_ui/library/edit_series_ui.rs b/src/ui/sonarr_ui/library/edit_series_ui.rs index f7086ba..40c7f45 100644 --- a/src/ui/sonarr_ui/library/edit_series_ui.rs +++ b/src/ui/sonarr_ui/library/edit_series_ui.rs @@ -99,7 +99,12 @@ fn draw_edit_series_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, ar use_season_folders, path, tags, - } = app.data.sonarr_data.edit_series_modal.as_ref().unwrap(); + } = app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .expect("edit_series_modal must exist in this context"); let selected_series_type = series_type_list.current_selection(); let selected_quality_profile = quality_profile_list.current_selection(); let selected_language_profile = language_profile_list.current_selection(); @@ -202,7 +207,7 @@ fn draw_edit_series_select_series_type_popup(f: &mut Frame<'_>, app: &mut App<'_ .sonarr_data .edit_series_modal .as_mut() - .unwrap() + .expect("edit_series_modal must exist in this context") .series_type_list, |series_type| ListItem::new(series_type.to_display_str().to_owned()), ); @@ -218,7 +223,7 @@ fn draw_edit_series_select_quality_profile_popup(f: &mut Frame<'_>, app: &mut Ap .sonarr_data .edit_series_modal .as_mut() - .unwrap() + .expect("edit_series_modal must exist in this context") .quality_profile_list, |quality_profile| ListItem::new(quality_profile.clone()), ); @@ -234,7 +239,7 @@ fn draw_edit_series_select_language_profile_popup(f: &mut Frame<'_>, app: &mut A .sonarr_data .edit_series_modal .as_mut() - .unwrap() + .expect("edit_series_modal must exist in this context") .language_profile_list, |language_profile| ListItem::new(language_profile.clone()), ); diff --git a/src/ui/sonarr_ui/library/episode_details_ui.rs b/src/ui/sonarr_ui/library/episode_details_ui.rs index 67196ac..73369b5 100644 --- a/src/ui/sonarr_ui/library/episode_details_ui.rs +++ b/src/ui/sonarr_ui/library/episode_details_ui.rs @@ -60,10 +60,10 @@ impl DrawUi for EpisodeDetailsUi { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .as_ref() - .unwrap() + .expect("episode_details_modal must exist in this context") .episode_details_tabs, ); draw_episode_details_tabs(f, app, content_area); @@ -77,7 +77,7 @@ impl DrawUi for EpisodeDetailsUi { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episodes .current_selection() .title @@ -181,7 +181,7 @@ fn draw_episode_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .is_none(), block, @@ -303,10 +303,10 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) .sonarr_data .season_details_modal .as_mut() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .as_mut() - .unwrap() + .expect("episode_details_modal must exist in this context") .episode_history; let history_table = ManagarrTable::new(Some(&mut episode_history_table), history_row_mapping) @@ -332,7 +332,7 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .is_none(), layout_block_top_border(), @@ -474,10 +474,10 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .season_details_modal .as_mut() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .as_mut() - .unwrap() + .expect("episode_details_modal must exist in this context") .episode_releases; let release_table = ManagarrTable::new( Some(&mut episode_release_table), @@ -513,7 +513,7 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .is_none(), layout_block_top_border(), @@ -529,10 +529,10 @@ fn draw_manual_episode_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_ .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episode_details_modal .as_ref() - .unwrap() + .expect("episode_details_modal must exist in this context") .episode_releases .current_selection(); let title = if current_selection.rejected { diff --git a/src/ui/sonarr_ui/library/mod.rs b/src/ui/sonarr_ui/library/mod.rs index 4fa6ddf..ba3c594 100644 --- a/src/ui/sonarr_ui/library/mod.rs +++ b/src/ui/sonarr_ui/library/mod.rs @@ -106,11 +106,11 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .map_or(0f64, |stats| convert_to_gb(stats.size_on_disk)); let quality_profile = quality_profile_map .get_by_left(&series.quality_profile_id) - .unwrap() + .expect("Quality profile ID must exist in quality_profile_map") .to_owned(); let language_profile = language_profile_map .get_by_left(&series.language_profile_id) - .unwrap() + .expect("Language profile ID must exist in language_profile_map") .to_owned(); let empty_tag = String::new(); let tags = if !series.tags.is_empty() { @@ -119,7 +119,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .iter() .map(|tag_id| { tags_map - .get_by_left(&tag_id.as_i64().unwrap()) + .get_by_left(&tag_id.as_i64().expect("Tag ID must be a valid i64")) .unwrap_or(&empty_tag) .clone() }) diff --git a/src/ui/sonarr_ui/library/season_details_ui.rs b/src/ui/sonarr_ui/library/season_details_ui.rs index 276e3be..fb0bb02 100644 --- a/src/ui/sonarr_ui/library/season_details_ui.rs +++ b/src/ui/sonarr_ui/library/season_details_ui.rs @@ -67,7 +67,7 @@ impl DrawUi for SeasonDetailsUi { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .season_details_tabs, ); draw_season_details(f, app, content_area); @@ -103,7 +103,7 @@ impl DrawUi for SeasonDetailsUi { .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .episodes .current_selection() .title @@ -294,7 +294,7 @@ fn draw_season_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .season_details_modal .as_mut() - .unwrap() + .expect("season_details_modal must exist in this context") .season_history; let history_table = ManagarrTable::new(Some(&mut season_history_table), history_row_mapping) @@ -426,7 +426,7 @@ fn draw_season_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .sonarr_data .season_details_modal .as_mut() - .unwrap() + .expect("season_details_modal must exist in this context") .season_releases; let release_table = ManagarrTable::new(Some(&mut season_release_table), season_release_row_mapping) @@ -467,7 +467,7 @@ fn draw_manual_season_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_> .sonarr_data .season_details_modal .as_ref() - .unwrap() + .expect("season_details_modal must exist in this context") .season_releases .current_selection(); let title = if current_selection.rejected { diff --git a/src/ui/sonarr_ui/library/series_details_ui.rs b/src/ui/sonarr_ui/library/series_details_ui.rs index b134403..d62d7e1 100644 --- a/src/ui/sonarr_ui/library/series_details_ui.rs +++ b/src/ui/sonarr_ui/library/series_details_ui.rs @@ -333,7 +333,12 @@ fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { ]) .primary() }; - let mut series_history_table = app.data.sonarr_data.series_history.as_mut().unwrap(); + let mut series_history_table = app + .data + .sonarr_data + .series_history + .as_mut() + .expect("series_history must be populated"); let history_table = ManagarrTable::new(Some(&mut series_history_table), history_row_mapping) .block(layout_block_top_border()) diff --git a/src/ui/styles.rs b/src/ui/styles.rs index 3bc4ef6..306f794 100644 --- a/src/ui/styles.rs +++ b/src/ui/styles.rs @@ -40,31 +40,94 @@ where } fn awaiting_import(self) -> T { - THEME.with(|theme| self.fg(theme.get().awaiting_import.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .awaiting_import + .expect("awaiting_import style must be defined in theme") + .color + .expect("awaiting_import color must be defined"), + ) + }) } fn indeterminate(self) -> T { - THEME.with(|theme| self.fg(theme.get().indeterminate.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .indeterminate + .expect("indeterminate style must be defined in theme") + .color + .expect("indeterminate color must be defined"), + ) + }) } fn default(self) -> T { - THEME.with(|theme| self.fg(theme.get().default.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .default + .expect("default style must be defined in theme") + .color + .expect("default color must be defined"), + ) + }) } fn downloaded(self) -> T { - THEME.with(|theme| self.fg(theme.get().downloaded.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .downloaded + .expect("downloaded style must be defined in theme") + .color + .expect("downloaded color must be defined"), + ) + }) } fn downloading(self) -> T { - THEME.with(|theme| self.fg(theme.get().downloading.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .downloading + .expect("downloading style must be defined in theme") + .color + .expect("downloading color must be defined"), + ) + }) } fn failure(self) -> T { - THEME.with(|theme| self.fg(theme.get().failure.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .failure + .expect("failure style must be defined in theme") + .color + .expect("failure color must be defined"), + ) + }) } fn help(self) -> T { - THEME.with(|theme| self.fg(theme.get().help.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .help + .expect("help style must be defined in theme") + .color + .expect("help color must be defined"), + ) + }) } fn highlight(self) -> T { @@ -72,38 +135,119 @@ where } fn missing(self) -> T { - THEME.with(|theme| self.fg(theme.get().missing.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .missing + .expect("missing style must be defined in theme") + .color + .expect("missing color must be defined"), + ) + }) } fn primary(self) -> T { - THEME.with(|theme| self.fg(theme.get().primary.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .primary + .expect("primary style must be defined in theme") + .color + .expect("primary color must be defined"), + ) + }) } fn secondary(self) -> T { - THEME.with(|theme| self.fg(theme.get().secondary.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .secondary + .expect("secondary style must be defined in theme") + .color + .expect("secondary color must be defined"), + ) + }) } fn success(self) -> T { - THEME.with(|theme| self.fg(theme.get().success.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .success + .expect("success style must be defined in theme") + .color + .expect("success color must be defined"), + ) + }) } fn system_function(self) -> T { - THEME.with(|theme| self.fg(theme.get().system_function.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .system_function + .expect("system_function style must be defined in theme") + .color + .expect("system_function color must be defined"), + ) + }) } fn unmonitored(self) -> T { - THEME.with(|theme| self.fg(theme.get().unmonitored.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .unmonitored + .expect("unmonitored style must be defined in theme") + .color + .expect("unmonitored color must be defined"), + ) + }) } fn unmonitored_missing(self) -> T { - THEME.with(|theme| self.fg(theme.get().unmonitored_missing.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .unmonitored_missing + .expect("unmonitored_missing style must be defined in theme") + .color + .expect("unmonitored_missing color must be defined"), + ) + }) } fn unreleased(self) -> T { - THEME.with(|theme| self.fg(theme.get().unreleased.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .unreleased + .expect("unreleased style must be defined in theme") + .color + .expect("unreleased color must be defined"), + ) + }) } fn warning(self) -> T { - THEME.with(|theme| self.fg(theme.get().warning.unwrap().color.unwrap())) + THEME.with(|theme| { + self.fg( + theme + .get() + .warning + .expect("warning style must be defined in theme") + .color + .expect("warning color must be defined"), + ) + }) } } diff --git a/src/utils.rs b/src/utils.rs index 0771535..d853e2f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -80,7 +80,7 @@ pub fn convert_runtime(runtime: i64) -> (i64, i64) { } pub async fn tail_logs(no_color: bool) { - let re = Regex::new(r"^(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+<(?P[^\s>]+)>\s+\[(?P[A-Z]+)\]\s+(?P[^:]+):(?P\d+)\s+-\s+(?P.*)$").unwrap(); + let re = Regex::new(r"^(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+<(?P[^\s>]+)>\s+\[(?P[A-Z]+)]\s+(?P[^:]+):(?P\d+)\s+-\s+(?P.*)$").unwrap(); let file_path = get_log_path(); let file = File::open(&file_path).expect("Cannot open file"); let mut reader = BufReader::new(file); @@ -311,11 +311,13 @@ pub fn select_cli_configuration( } else { match command { Command::Radarr(_) => { - let default_radarr_config = config.radarr.as_ref().unwrap()[0].clone(); + let default_radarr_config = + config.radarr.as_ref().expect("Radarr config must exist")[0].clone(); app.server_tabs.select_tab_by_config(&default_radarr_config); } Command::Sonarr(_) => { - let default_sonarr_config = config.sonarr.as_ref().unwrap()[0].clone(); + let default_sonarr_config = + config.sonarr.as_ref().expect("Sonarr config must exist")[0].clone(); app.server_tabs.select_tab_by_config(&default_sonarr_config); } _ => (),