use error_tools::{ Error, Result };
const BOOL_PARAMS : &[ &str ] = &[
"dry",
"active",
"account", "sub", "tier", "token", "expires", "email", "file", "saved",
];
const VERBOSITY_ALIAS : &str = "v";
const VERBOSITY_KEY : &str = "verbosity";
const POSITIONAL_NAME_COMMANDS : &[ &str ] = &[
".accounts", ".account.use", ".account.delete", ".account.limits", ".account.relogin",
];
const FORMAT_ALIAS : &str = "fmt";
const FORMAT_KEY : &str = "format";
#[ inline ]
fn split_first_colons( s : &str ) -> Option< ( &str, &str ) >
{
s.find( "::" ).map( |i| ( &s[ ..i ], &s[ i + 2.. ] ) )
}
#[ inline ]
fn normalise_bool_value( key : &str, raw_val : &str ) -> Result< String >
{
match raw_val
{
"true" | "1" => Ok( "1".to_string() ),
"false" | "0" => Ok( "0".to_string() ),
other =>
{
Err( Error::msg( format!(
"invalid value for {key}::{other}: expected true, false, 1, or 0"
) ) )
}
}
}
#[ inline ]
pub fn argv_to_unilang_tokens( argv : &[ String ] ) -> Result< ( Vec< String >, bool ) >
{
if argv.is_empty()
{
return Ok( ( vec![ ".help".to_string() ], true ) );
}
if argv.iter().any( |a| a == ".help" || a == "help" )
{
return Ok( ( vec![ ".help".to_string() ], true ) );
}
if argv[ 0 ].starts_with( '-' )
{
return Err( Error::msg( format!(
"unexpected flag '{}': use param::value syntax (e.g., verbosity::2)",
argv[ 0 ]
) ) );
}
if argv[ 0 ].contains( "::" )
{
return Err( Error::msg( format!(
"expected command name as first argument, got '{}'",
argv[ 0 ]
) ) );
}
if argv[ 0 ] == "."
{
return Ok( ( vec![ ".help".to_string() ], true ) );
}
let command_name = argv[ 0 ].clone();
let rewritten_args : Option< Vec< String > > =
if POSITIONAL_NAME_COMMANDS.contains( &command_name.as_str() )
{
argv[ 1.. ].iter()
.position( |a| !a.contains( "::" ) && !a.starts_with( '-' ) )
.map( |idx|
{
let name_arg = format!( "name::{}", argv[ idx + 1 ] );
core::iter::once( name_arg )
.chain(
argv[ 1.. ].iter().enumerate()
.filter( |( i, _ )| *i != idx )
.map( |( _, a )| a.clone() ),
)
.collect()
} )
}
else { None };
let rest : &[ String ] = rewritten_args.as_deref().unwrap_or( &argv[ 1.. ] );
let mut pairs : Vec< ( String, String ) > = vec![];
for arg in rest
{
if arg.starts_with( '-' )
{
return Err( Error::msg( format!(
"unexpected flag '{arg}': use param::value syntax (e.g., verbosity::2)"
) ) );
}
let ( raw_key, raw_val ) = split_first_colons( arg ).ok_or_else( || Error::msg( format!(
"expected param::value syntax, got: '{arg}'"
) ) )?;
let key : String = if raw_key == VERBOSITY_ALIAS
{
VERBOSITY_KEY.to_string()
}
else if raw_key == FORMAT_ALIAS
{
FORMAT_KEY.to_string()
}
else
{
raw_key.to_string()
};
let val : String = if BOOL_PARAMS.contains( &key.as_str() )
{
normalise_bool_value( &key, raw_val )?
}
else
{
raw_val.to_string()
};
if let Some( entry ) = pairs.iter_mut().find( |( k, _ )| k == &key )
{
entry.1 = val;
}
else
{
pairs.push( ( key, val ) );
}
}
let mut tokens = vec![ command_name ];
for ( k, v ) in pairs
{
tokens.push( format!( "{k}::{v}" ) );
}
Ok( ( tokens, false ) )
}