pub( crate ) mod private
{
use crate::command::*;
use crate::instruction::*;
use wtools::meta::*;
use wtools::error::*;
use wtools::string::split;
use wtools::former::Former;
#[ derive( Debug, PartialEq ) ]
#[ derive( Former ) ]
#[ allow( missing_docs ) ]
pub struct CommandsAggregator
{
pub base_path : Option<std::path::PathBuf>,
#[ default( "".to_string() ) ]
pub command_prefix : String,
#[ default( vec![ ".".to_string(), " ".to_string() ] ) ]
pub delimeter : Vec< String >,
#[ default( ";".to_string() ) ]
pub command_explicit_delimeter : String,
#[ default( " ".to_string() ) ]
pub command_implicit_delimeter : String,
#[ default( true ) ]
pub commands_explicit_delimiting : bool,
#[ default( false ) ]
pub commands_implicit_delimiting : bool,
#[ default( false ) ]
pub properties_map_parsing : bool,
#[ default( true ) ]
pub several_values : bool,
#[ default( true ) ]
pub with_help : bool,
#[ default( true ) ]
pub changing_exit_code : bool,
pub commands : std::collections::HashMap< String, Command >,
}
impl CommandsAggregator
{
pub fn program_perform( &self, program : impl AsRef< str > ) -> Result< (), BasicError >
{
let program = program.as_ref().trim();
if !program.starts_with( '.' )
|| program.starts_with( "./" )
|| program.starts_with( ".\\" )
{
return self.on_syntax_error( program );
}
println!( "Command \"{}\"", program );
let instructions = self.instructions_parse( program );
for instruction in &instructions
{
match self._instruction_perform( instruction )
{
Ok( _ ) => {},
Err( err ) =>
{
if self.changing_exit_code
{
eprintln!( "{}", err.to_string() );
std::process::exit( 1 );
}
else
{
return Err( err )
}
}
}
}
Ok( () )
}
pub fn instruction_perform( &self, instruction : impl AsRef< str > ) -> Result< (), BasicError >
{
let parsed : Instruction = instruction_parse()
.instruction( instruction.as_ref() )
.several_values( self.several_values )
.properties_map_parsing( self.properties_map_parsing )
.quoting( true )
.unquoting( true )
.perform();
self._instruction_perform( &parsed )
}
fn _instruction_perform( &self, instruction : &Instruction ) -> Result< (), BasicError >
{
let result = match self.command_resolve( instruction )
{
Some( command ) =>
{
command.perform( instruction )
},
None =>
{
if self.with_help
{
match self.on_ambiguity( instruction.command_name.as_str() )
{
_ => (),
}
}
if self.changing_exit_code
{
std::process::exit( 1 );
}
Ok( () )
},
};
result
}
fn command_help( &self, command : impl AsRef< str > )
{
if command.as_ref() == ""
{
for ( _name, command_descriptor ) in self.commands.iter()
{
println!( "{}", command_descriptor.help_short() );
}
}
else
{
if let Some( command_descriptor ) = self.commands.get( command.as_ref() )
{
println!( "{}", command_descriptor.help_long() );
}
else
{
match self.on_unknown_command_error( command.as_ref() )
{
_ => ()
};
}
}
}
fn command_resolve( &self, instruction : &Instruction ) -> Option<&Command>
{
self.commands.get( &instruction.command_name )
}
pub fn instructions_parse( &self, program : impl AsRef< str > ) -> Vec< Instruction >
{
let commands = split()
.src( program.as_ref().trim() )
.delimeter( self.command_explicit_delimeter.as_str() )
.preserving_empty( false )
.preserving_delimeters( false )
.preserving_quoting( false )
.perform();
let commands = commands.map( | e | String::from( e ) ).collect::< Vec< _ > >();
let mut string_commands = vec![];
for command in commands
{
let splitted = split()
.src( command.trim() )
.delimeter( self.command_implicit_delimeter.as_str() )
.preserving_empty( false )
.preserving_delimeters( false )
.preserving_quoting( false )
.perform();
let splitted = splitted.map( | e | String::from( e ) ).collect::< Vec< _ > >();
if self.command_implicit_delimeter == " "
{
let start_index = if splitted[ 0 ].is_empty() { 1 } else { 0 };
let mut string_command = String::from( &splitted[ start_index ] );
for i in start_index + 1 .. splitted.len()
{
let part = splitted[ i ].trim();
if part.starts_with( '.' ) && !self.dotted_path_is( part )
{
string_commands.push( string_command );
string_command = String::from( part );
}
else
{
string_command.push( ' ' );
string_command.push_str( part );
}
}
string_commands.push( string_command );
}
else
{
for command in splitted
{
string_commands.push( String::from( command.trim() ) );
}
}
}
let instructions = string_commands.iter().map( | instruction |
{
instruction_parse()
.instruction( instruction.as_str() )
.properties_map_parsing( true )
.several_values( true )
.perform()
}).collect::< Vec< Instruction > >();
instructions
}
fn dotted_path_is( &self, src : impl AsRef< str > ) -> bool
{
let part = src.as_ref();
if part == "." || part == ".."
{
return true;
}
if part.starts_with( "./" ) || part.starts_with( "../" )
|| part.starts_with( ".\\" ) || part.starts_with( "..\\" )
{
return true;
}
false
}
}
pub trait OnError
{
fn on_error( &self, err : BasicError ) -> Result< (), BasicError >;
}
pub trait OnSyntaxError
{
fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
}
pub trait OnAmbiguity
{
fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
}
pub trait OnUnknownCommandError
{
fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >;
}
pub trait OnGetHelp
{
fn on_get_help( &self ) -> Result< (), BasicError >;
}
pub trait OnPrintCommands
{
fn on_print_commands( &self ) -> Result< (), BasicError >;
}
pub trait CommandsAggregatorHandlers : OnError + OnSyntaxError + OnAmbiguity + OnUnknownCommandError + OnGetHelp + OnPrintCommands
{
}
impl CommandsAggregatorHandlers for CommandsAggregator {}
#[ cfg( feature = "on_error_default" ) ]
impl OnError for CommandsAggregator
{
fn on_error( &self, err : BasicError ) -> Result< (), BasicError >
{
if self.changing_exit_code
{
}
Err( err )
}
}
#[ cfg( feature = "on_syntax_error_default" ) ]
impl OnSyntaxError for CommandsAggregator
{
fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
{
let err_formatted = format!( "Illformed command \"{}\"", command.as_ref() );
eprintln!( "{}", err_formatted );
self.on_get_help().unwrap();
let err = BasicError::new( err_formatted );
return self.on_error( err );
}
}
#[ cfg( feature = "on_ambiguity_default" ) ]
impl OnAmbiguity for CommandsAggregator
{
fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
{
eprintln!( "Ambiguity. Did you mean?" );
self.command_help( command.as_ref() );
println!( "" );
let err_formatted = format!( "Ambiguity \"{}\"", command.as_ref() );
let err = BasicError::new( err_formatted );
return self.on_error( err );
}
}
#[ cfg( feature = "on_unknown_command_error_default" ) ]
impl OnUnknownCommandError for CommandsAggregator
{
fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >
{
let mut err_formatted = format!( "Unknown command \"{}\"", command.as_ref() );
let instruction = instruction_parse()
.instruction( ".help" )
.perform();
if self.command_resolve( &instruction ).is_some()
{
err_formatted.push_str( "\nTry \".help\"" );
}
let err = BasicError::new( err_formatted );
return self.on_error( err );
}
}
#[ cfg( feature = "on_get_help_default" ) ]
impl OnGetHelp for CommandsAggregator
{
fn on_get_help( &self ) -> Result< (), BasicError >
{
let instruction = instruction_parse()
.instruction( ".help" )
.perform();
if let Some( command ) = self.command_resolve( &instruction )
{
let instruction = instruction_parse()
.instruction( "" )
.perform();
return command.perform( &instruction );
}
else
{
self.command_help( "" );
return Ok( () );
}
}
}
#[ cfg( feature = "on_print_commands_default" ) ]
impl OnPrintCommands for CommandsAggregator
{
fn on_print_commands( &self ) -> Result< (), BasicError >
{
println!( "" );
self.command_help( "" );
println!( "" );
Ok( () )
}
}
pub fn commands_aggregator() -> CommandsAggregatorFormer
{
CommandsAggregator::former()
}
}
pub mod protected
{
pub use super::private::CommandsAggregator;
pub use super::private::OnError;
pub use super::private::OnSyntaxError;
pub use super::private::OnAmbiguity;
pub use super::private::OnUnknownCommandError;
pub use super::private::OnGetHelp;
pub use super::private::OnPrintCommands;
pub use super::private::commands_aggregator;
}
pub use protected::*;
pub mod exposed
{
pub use super::prelude::*;
}
pub mod prelude
{
pub use super::private::CommandsAggregator;
pub use super::private::OnError;
pub use super::private::OnSyntaxError;
pub use super::private::OnAmbiguity;
pub use super::private::OnUnknownCommandError;
pub use super::private::OnGetHelp;
pub use super::private::OnPrintCommands;
pub use super::private::commands_aggregator;
}