feat: CLI Support for multiple Servarr instances

This commit is contained in:
2025-02-27 20:37:03 -07:00
parent f87e02cd7c
commit fd6fcfc98f
9 changed files with 239 additions and 40 deletions
+3 -3
View File
@@ -65,7 +65,7 @@ impl App<'_> {
idx+=1;
format!("Radarr {}", idx)
};
server_tabs.push(TabRoute {
title: name,
route: ActiveRadarrBlock::Movies.into(),
@@ -78,7 +78,7 @@ impl App<'_> {
if let Some(sonarr_configs) = config.sonarr {
let mut idx = 0;
for sonarr_config in sonarr_configs {
let name = if let Some(name) = sonarr_config.name.clone() {
name
@@ -86,7 +86,7 @@ impl App<'_> {
idx+=1;
format!("Sonarr {}", idx)
};
server_tabs.push(TabRoute {
title: name,
route: ActiveSonarrBlock::Series.into(),
+11 -3
View File
@@ -73,7 +73,15 @@ struct Cli {
env = "MANAGARR_CONFIG_FILE",
help = "The Managarr configuration file to use"
)]
config: Option<PathBuf>,
config_file: Option<PathBuf>,
#[arg(
long,
global = true,
help = "For multi-instance configurations, you need to specify the name of the instance configuration that you want to use.
This is useful when you have multiple instances of the same Servarr defined in your config file.
By default, if left empty, the first configured Servarr instance listed in the config file will be used."
)]
servarr_name: Option<String>,
}
#[tokio::main]
@@ -85,7 +93,7 @@ async fn main() -> Result<()> {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
let args = Cli::parse();
let mut config = if let Some(ref config_file) = args.config {
let mut config = if let Some(ref config_file) = args.config_file {
load_config(config_file.to_str().expect("Invalid config file specified"))?
} else {
confy::load("managarr", "config")?
@@ -111,7 +119,7 @@ async fn main() -> Result<()> {
config.clone(),
cancellation_token.clone(),
)));
match args.command {
Some(command) => match command {
Command::Radarr(_) | Command::Sonarr(_) => {
+33 -1
View File
@@ -303,7 +303,39 @@ impl TabState {
&self.tabs[self.index].config
}
pub fn select_tab_by_title(&mut self, name: &str) -> bool {
if !self.tabs.is_empty() {
let mut found = false;
self.tabs.iter().enumerate().for_each(|(idx, tab)| {
if tab.title == name {
self.index = idx;
found = true;
}
});
return found;
}
false
}
pub fn select_tab_by_config(&mut self, config: &ServarrConfig) -> bool {
if !self.tabs.is_empty() {
let mut found = false;
self.tabs.iter().enumerate().for_each(|(idx, tab)| {
if tab.config == Some(config.clone()) {
self.index = idx;
found = true;
}
});
return found;
}
false
}
pub fn get_active_tab_help(&self) -> &str {
&self.tabs[self.index].help
}
+72
View File
@@ -537,6 +537,78 @@ mod tests {
assert!(active_config.is_none());
}
#[test]
fn test_select_tab_by_title() {
let tabs = create_test_tab_routes();
let mut tab_state = TabState { tabs, index: 0 };
let result = tab_state.select_tab_by_title("Test 2");
assert!(result);
assert_eq!(tab_state.index, 1);
let result = tab_state.select_tab_by_title("Not real");
assert!(!result);
assert_eq!(tab_state.index, 1);
}
#[test]
fn test_select_tab_by_title_empty_tabs_returns_false() {
let mut tab_state = TabState { tabs: vec![], index: 0 };
let result = tab_state.select_tab_by_title("Test 2");
assert!(!result);
assert_eq!(tab_state.index, 0);
}
#[test]
fn test_select_tab_by_config() {
let mut tabs = create_test_tab_routes();
tabs[0].config = Some(ServarrConfig {
name: Some("Test 1".to_owned()),
..ServarrConfig::default()
});
tabs[1].config = Some(ServarrConfig {
host: Some("http://localhost".to_owned()),
port: Some(7878),
..ServarrConfig::default()
});
let mut tab_state = TabState { tabs, index: 0 };
let result = tab_state.select_tab_by_config(&ServarrConfig {
host: Some("http://localhost".to_owned()),
port: Some(7878),
..ServarrConfig::default()
});
assert!(result);
assert_eq!(tab_state.index, 1);
let result = tab_state.select_tab_by_config(&ServarrConfig {
name: Some("Not real".to_owned()),
..ServarrConfig::default()
});
assert!(!result);
assert_eq!(tab_state.index, 1);
}
#[test]
fn test_select_tab_by_config_empty_tabs_returns_false() {
let mut tab_state = TabState { tabs: vec![], index: 0 };
let result = tab_state.select_tab_by_config(&ServarrConfig {
host: Some("http://localhost".to_owned()),
port: Some(7878),
..ServarrConfig::default()
});
assert!(!result);
assert_eq!(tab_state.index, 0);
}
#[test]
fn test_tab_state_get_active_tab_help() {
+32 -2
View File
@@ -227,7 +227,11 @@ pub(super) async fn start_cli_with_spinner(
command: Command,
) {
config.verify_config_present_for_cli(&command);
app.lock().await.cli_mode = true;
{
let mut app = app.lock().await;
app.cli_mode = true;
select_cli_configuration(&mut app, &config, &command, None);
}
let pb = render_spinner();
let app_nw = Arc::clone(&app);
let mut network = Network::new(&app_nw, cancellation_token, reqwest_client);
@@ -252,7 +256,11 @@ pub(super) async fn start_cli_no_spinner(
command: Command,
) {
config.verify_config_present_for_cli(&command);
app.lock().await.cli_mode = true;
{
let mut app = app.lock().await;
app.cli_mode = true;
select_cli_configuration(&mut app, &config, &command, None);
}
let app_nw = Arc::clone(&app);
let mut network = Network::new(&app_nw, cancellation_token, reqwest_client);
match cli::handle_command(&app, command, &mut network).await {
@@ -265,3 +273,25 @@ pub(super) async fn start_cli_no_spinner(
}
}
}
pub fn select_cli_configuration(app: &mut App<'_>, config: &AppConfig, command: &Command, servarr_name_arg: Option<String>) {
if let Some(servarr_name) = servarr_name_arg {
let trimmed_name = servarr_name.trim();
if !app.server_tabs.select_tab_by_title(trimmed_name) {
log_and_print_error(format!("A Servarr titled '{}' was not found in your configuration file", trimmed_name));
process::exit(1);
}
} else {
match command {
Command::Radarr(_) => {
let default_radarr_config = config.radarr.as_ref().unwrap()[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();
app.server_tabs.select_tab_by_config(&default_sonarr_config);
},
_ => ()
}
}
}