use std::collections::BTreeSet;
use unilang_parser::cli_parser ::
{
parse_cli_args, CliParams, CliParseResult,
CliParser, CliParamsAdvanced, CliParseResultAdvanced,
};
#[ derive( Default, Debug ) ]
struct SimpleParams
{
scope : Option< String >,
path : Option< String >,
}
impl CliParams for SimpleParams
{
fn process_param( &mut self, key : &str, value : &str ) -> Result< bool, String >
{
match key
{
"scope" => { self.scope = Some( value.to_string() ); Ok( true ) }
"path" => { self.path = Some( value.to_string() ); Ok( true ) }
_ => Ok( false ),
}
}
fn validate( &self ) -> Result< (), String >
{
Ok( () )
}
}
struct EmptyConfig;
#[ derive( Default, Debug ) ]
struct AdvancedParams
{
scope : Option< String >,
path : Option< String >,
}
impl CliParamsAdvanced< EmptyConfig > for AdvancedParams
{
fn process_param( &mut self, key : &str, value : &str ) -> Result< Option< &'static str >, String >
{
match key
{
"scope" => { self.scope = Some( value.to_string() ); Ok( Some( "scope" ) ) }
"path" => { self.path = Some( value.to_string() ); Ok( Some( "path" ) ) }
_ => Ok( None ),
}
}
fn apply_defaults( &mut self, _config : &EmptyConfig, _explicit : &BTreeSet< String > ) {}
fn finalize( &mut self, _explicit : &BTreeSet< String >, _message : &str ) {}
fn validate( &self ) -> Result< (), String >
{
Ok( () )
}
}
#[ test ]
fn t01_single_colon_parse_cli_args()
{
let args = vec![ "scope:local".to_string() ];
let result : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result.is_err(), "scope:local must be rejected by parse_cli_args" );
let err = result.unwrap_err();
assert!(
err.contains( "::" ) || err.contains( "separator" ) || err.contains( "syntax" ),
"error must mention '::' or separator. Got: {err}"
);
}
#[ test ]
fn t02_single_colon_cliparser_parse()
{
let config = EmptyConfig;
let args = vec![ "scope:local".to_string() ];
let result : Result< CliParseResultAdvanced< AdvancedParams >, String > = CliParser ::new()
.with_config( &config )
.parse( &args );
assert!( result.is_err(), "scope:local must be rejected by CliParser::parse for consistency with parse_cli_args" );
let err = result.unwrap_err();
assert!(
err.contains( "::" ) || err.contains( "separator" ) || err.contains( "syntax" ),
"error must mention '::' or separator. Got: {err}"
);
}
#[ test ]
fn t03_equals_sign_parse_cli_args()
{
let args = vec![ "timeout=5000".to_string() ];
let result : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result.is_err(), "timeout=5000 must be rejected by parse_cli_args; use 'timeout::5000'" );
let err = result.unwrap_err();
assert!(
err.contains( "::" ) || err.contains( "name::value" ) || err.contains( "separator" ),
"error must hint at '::' syntax. Got: {err}"
);
}
#[ test ]
fn t04_equals_sign_cliparser_parse()
{
let config = EmptyConfig;
let args = vec![ "timeout=5000".to_string() ];
let result : Result< CliParseResultAdvanced< AdvancedParams >, String > = CliParser ::new()
.with_config( &config )
.parse( &args );
assert!( result.is_err(), "timeout=5000 must be rejected by CliParser::parse" );
let err = result.unwrap_err();
assert!(
err.contains( "::" ) || err.contains( "name::value" ) || err.contains( "separator" ),
"error must hint at '::' syntax. Got: {err}"
);
}
#[ test ]
fn t05_double_dash_flag_parse_cli_args()
{
let args = vec![ "--verbose".to_string() ];
let result : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result.is_err(), "--verbose must be rejected; unilang uses named params not --flag" );
let err = result.unwrap_err();
assert!(
err.contains( "named" ) || err.contains( "::" ) || err.contains( "--" ),
"error must hint at named parameter syntax. Got: {err}"
);
}
#[ test ]
fn t06_single_dash_flag_parse_cli_args()
{
let args = vec![ "-v".to_string() ];
let result : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result.is_err(), "-v must be rejected; unilang uses named params not -f short flags" );
let err = result.unwrap_err();
assert!(
err.contains( "named" ) || err.contains( "::" ) || err.contains( "-" ),
"error must hint at named parameter syntax. Got: {err}"
);
}
#[ test ]
fn t07_valid_double_colon_both_paths()
{
let args = vec![ "scope::local".to_string() ];
let result1 : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result1.is_ok(), "scope::local must parse via parse_cli_args: {:?}", result1.err() );
assert_eq!( result1.unwrap().params.scope, Some( "local".to_string() ) );
let config = EmptyConfig;
let result2 : Result< CliParseResultAdvanced< AdvancedParams >, String > = CliParser ::new()
.with_config( &config )
.parse( &args );
assert!( result2.is_ok(), "scope::local must parse via CliParser::parse: {:?}", result2.err() );
assert_eq!( result2.unwrap().params.scope, Some( "local".to_string() ) );
}
#[ test ]
fn t08_valid_path_value_with_slashes_both_paths()
{
let args = vec![ "path::tests/file.md".to_string() ];
let result1 : Result< CliParseResult< SimpleParams >, String > = parse_cli_args( &args );
assert!( result1.is_ok(), "path::tests/file.md must parse via parse_cli_args: {:?}", result1.err() );
assert_eq!( result1.unwrap().params.path, Some( "tests/file.md".to_string() ) );
let config = EmptyConfig;
let result2 : Result< CliParseResultAdvanced< AdvancedParams >, String > = CliParser ::new()
.with_config( &config )
.parse( &args );
assert!( result2.is_ok(), "path::tests/file.md must parse via CliParser::parse: {:?}", result2.err() );
assert_eq!( result2.unwrap().params.path, Some( "tests/file.md".to_string() ) );
}