use embedded_cli::Command; use rstest::rstest; use crate::impl_convert; use crate::wrapper::CliWrapper; use crate::terminal::assert_terminal; #[derive(Debug, Clone, Command, PartialEq)] enum CliBase<'a> { #[command(name = "base1")] Base1 { #[arg(short, long)] name: Option<&'a str>, #[arg(short, long)] level: u8, #[arg(short)] verbose: bool, #[command(subcommand)] command: CliBase1Sub<'a>, }, #[command(name = "base2", subcommand)] Base2(CliBase2Sub<'a>), } #[derive(Debug, Clone, Command, PartialEq)] enum CliBase1Sub<'a> { Get { #[arg(short, long)] item: Option<&'a str>, #[arg(short, long)] verbose: bool, file: &'a str, }, Set { value: &'a str, }, } #[derive(Debug, Clone, Command, PartialEq)] enum CliBase2Sub<'a> { Get { #[arg(short, long)] item: Option<&'a str>, #[arg(short, long)] verbose: bool, file: &'a str, }, Write { line: &'a str, }, } #[derive(Debug, Clone, PartialEq)] enum Base { Base1 { name: Option, level: u8, verbose: bool, command: Base1Sub, }, Base2(Base2Sub), } #[derive(Debug, Clone, PartialEq)] enum Base1Sub { Get { item: Option, verbose: bool, file: String, }, Set { value: String, }, } #[derive(Debug, Clone, PartialEq)] enum Base2Sub { Get { item: Option, verbose: bool, file: String, }, Write { line: String, }, } impl_convert! {CliBase<'_> => Base, command, { command.into() }} impl<'a> From> for Base { fn from(value: CliBase<'a>) -> Self { match value { CliBase::Base1 { name, level, verbose, command, } => Self::Base1 { name: name.map(|n| n.to_string()), level, verbose, command: command.into(), }, CliBase::Base2(command) => Self::Base2(command.into()), } } } impl<'a> From> for Base1Sub { fn from(value: CliBase1Sub<'a>) -> Self { match value { CliBase1Sub::Get { item, verbose, file, } => Self::Get { item: item.map(|n| n.to_string()), verbose, file: file.to_string(), }, CliBase1Sub::Set { value } => Self::Set { value: value.to_string(), }, } } } impl<'a> From> for Base2Sub { fn from(value: CliBase2Sub<'a>) -> Self { match value { CliBase2Sub::Get { item, verbose, file, } => Self::Get { item: item.map(|n| n.to_string()), verbose, file: file.to_string(), }, CliBase2Sub::Write { line } => Self::Write { line: line.to_string(), }, } } } #[rstest] #[case("base1 --name test-name --level 23 -v get --item config -v some-file", Base::Base1 { name: Some("test-name".to_string()), level: 23, verbose: true, command: Base1Sub::Get { item: Some("config".to_string()), verbose: true, file: "some-file".to_string(), } })] #[case("base1 -v --level 24 --name test-name set some-value", Base::Base1 { name: Some("test-name".to_string()), level: 24, verbose: true, command: Base1Sub::Set { value: "some-value".to_string(), } })] #[case("base2 get --item config -v some-file", Base::Base2 ( Base2Sub::Get { item: Some("config".to_string()), verbose: true, file: "some-file".to_string(), } ))] #[case("base2 write lines", Base::Base2 ( Base2Sub::Write { line: "lines".to_string(), } ))] fn options_parsing(#[case] command: &str, #[case] expected: Base) { let mut cli = CliWrapper::new(); cli.process_str(command); cli.send_enter(); assert_terminal!( cli.terminal(), 2, vec![format!("$ {}", command), "$".to_string()] ); assert_eq!(cli.received_commands(), vec![Ok(expected)]); }