mod gen_agent_certs; mod gen_certs; use clap::{Parser, Subcommand}; use crate::{ cli::{gen_agent_certs::gen_agent_certs, gen_certs::gen_certs}, config::settings::Settings, }; #[derive(Parser)] #[command(version, about, long_about = None)] pub struct Cli { /// Start the master server #[arg(short, long, group = "mode")] pub serve: bool, /// generate CA for key signing if not exist /// If the CA already exists, generating CA will be skipped and the existing CA will be used /// If the CA does not exist, a new CA will be generated and saved to the default location (./certs/ca.crt and ./certs/ca.key) /// The generated CA will be used for signing agent certificates /// If not specified, the server will check if the CA already exists and use it if available, otherwise exit with an error #[arg(long)] pub generate_ca: bool, #[command(subcommand)] pub command: Option, } #[derive(Subcommand)] pub enum Commands { GenCerts { /// Output directory for generated certificates #[arg(short, long, default_value = "./certs")] output: String, }, /// Generate certificates for agent #[command(about = "Generate certificates for agent")] GenAgentCerts { /// Output directory for generated certificates #[arg(short, long, default_value = "./certs")] output: String, #[arg(long, default_value = "agent-id-placeholder")] agent_id: String, #[arg(short, long, default_value = "false")] zip: bool, }, } pub async fn handle_sub_command( settings: &Settings, command: Commands, ) -> Result<(), Box> { // run as a CLI tool for other commands match command { Commands::GenCerts { output } => Ok(gen_certs(settings, output).await?), Commands::GenAgentCerts { output, agent_id, zip, } => Ok(gen_agent_certs(settings, output, agent_id, zip).await?), } } #[cfg(test)] mod tests { use clap::Parser; use super::{Cli, Commands}; #[test] fn parses_serve_mode() { let parsed = Cli::try_parse_from(["nxmesh-master", "--serve"]); assert!(parsed.is_ok()); let parsed = parsed.unwrap_or_else(|_| unreachable!()); assert!(parsed.serve); assert!(!parsed.generate_ca); assert!(parsed.command.is_none()); } #[test] fn parses_generate_ca_flag() { let parsed = Cli::try_parse_from(["nxmesh-master", "--generate-ca", "--serve"]); assert!(parsed.is_ok()); let parsed = parsed.unwrap_or_else(|_| unreachable!()); assert!(parsed.generate_ca); assert!(parsed.serve); } #[test] fn parses_gen_certs_with_default_output() { let parsed = Cli::try_parse_from(["nxmesh-master", "gen-certs"]); assert!(parsed.is_ok()); let parsed = parsed.unwrap_or_else(|_| unreachable!()); match parsed.command { Some(Commands::GenCerts { output }) => { assert_eq!(output, "./certs"); } _ => unreachable!(), } } #[test] fn parses_gen_agent_certs_with_custom_values() { let parsed = Cli::try_parse_from([ "nxmesh-master", "gen-agent-certs", "--output", "./out", "--agent-id", "agent-123", "--zip", ]); assert!(parsed.is_ok()); let parsed = parsed.unwrap_or_else(|_| unreachable!()); match parsed.command { Some(Commands::GenAgentCerts { output, agent_id, zip, }) => { assert_eq!(output, "./out"); assert_eq!(agent_id, "agent-123"); assert!(zip); } _ => unreachable!(), } } }