pub( crate ) mod private { use crate::command::*; use crate::instruction::*; use wtools::meta::*; use wtools::error::*; use wtools::string::split; use wtools::former::Former; /// /// Commands aggregator. /// /* Dmytro : owned types are used because Former does not work with combination of links and containers */ #[ derive( Debug, PartialEq ) ] #[ derive( Former ) ] #[ allow( missing_docs ) ] pub struct CommandsAggregator { pub base_path : Option, #[ 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, // logger : Option, /* qqq : implement */ pub commands : std::collections::HashMap< String, Command >, // pub vocabulary : Option, /* qqq : implement */ } impl CommandsAggregator { /// Perform instructions queue as single program. pub fn program_perform( &self, program : impl AsRef< str > ) -> Result< (), BasicError > { let program = program.as_ref().trim(); if !program.starts_with( '.' /* aggregator.vocabulary.default_delimeter */ ) || program.starts_with( "./" /* `${aggregator.vocabulary.default_delimeter}/` */ ) || program.starts_with( ".\\" /* `${aggregator.vocabulary.default_delimeter}\\` */ ) { return self.on_syntax_error( program ); } /* should use logger and condition */ 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( () ) } /// Perform instruction. 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 } /// Print help for command. 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() ) { _ => () }; } } } /// Find command in dictionary. fn command_resolve( &self, instruction : &Instruction ) -> Option<&Command> { self.commands.get( &instruction.command_name ) } /// Parse multiple instructions. 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 } } // /// /// On error helper. /// pub trait OnError { /// Handle error. fn on_error( &self, err : BasicError ) -> Result< (), BasicError >; } /// /// On syntax error helper. /// pub trait OnSyntaxError { /// Handle syntax error. fn on_syntax_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >; } /// /// On ambiguity helper. /// pub trait OnAmbiguity { /// Handle ambiguity. fn on_ambiguity( &self, command : impl AsRef< str > ) -> Result< (), BasicError >; } /// /// On unknown command error helper. /// pub trait OnUnknownCommandError { /// Handle unknown command error. fn on_unknown_command_error( &self, command : impl AsRef< str > ) -> Result< (), BasicError >; } /// /// Help helper. /// pub trait OnGetHelp { /// Get help. fn on_get_help( &self ) -> Result< (), BasicError >; } /// /// Printing commands helper. /// pub trait OnPrintCommands { /// Print all commands. fn on_print_commands( &self ) -> Result< (), BasicError >; } /// /// Super trait that checks that all helpers are implemented. /// pub trait CommandsAggregatorHandlers : OnError + OnSyntaxError + OnAmbiguity + OnUnknownCommandError + OnGetHelp + OnPrintCommands { } impl CommandsAggregatorHandlers for CommandsAggregator {} #[ cfg( feature = "on_error_default" ) ] impl OnError for CommandsAggregator { /// Handle error. fn on_error( &self, err : BasicError ) -> Result< (), BasicError > { if self.changing_exit_code { /* qqq : implement */ // unimplemented!(); } Err( err ) } } #[ cfg( feature = "on_syntax_error_default" ) ] impl OnSyntaxError for CommandsAggregator { /// Handle syntax error. 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 { /// Handle ambiguity. 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 { /// Handle unknown command error. 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 { /// Get help. 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 { /// Print all commands. fn on_print_commands( &self ) -> Result< (), BasicError > { println!( "" ); self.command_help( "" ); println!( "" ); Ok( () ) } } // /// /// Get instruction parser builder. /// pub fn commands_aggregator() -> CommandsAggregatorFormer { CommandsAggregator::former() } } /// Protected namespace of the module. 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::*; /// Exposed namespace of the module. pub mod exposed { pub use super::prelude::*; } /// Namespace of the module to include with `use module::*`. 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; }