diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index 9a06ac3547..371e32bb3d 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -3,16 +3,16 @@ pub( crate ) mod private use crate::*; use ca:: { - Parser, Verifier, ExecutorConverter, + Parser, Verifier,// ExecutorConverter, Executor, ProgramParser, Command, grammar::command::private::CommandFormer, - Routine, - help::{ HelpGeneratorFn, HelpVariants, dot_command }, + // Routine, + // help::{ HelpGeneratorFn, HelpVariants, dot_command }, }; - use std::collections::{ HashMap, HashSet }; + // use std::collections::{ HashMap, HashSet }; use std::fmt; use wtools::thiserror; use wtools::error:: @@ -60,7 +60,7 @@ pub( crate ) mod private // xxx : qqq : qqq2 : for Bohdan : one level is obviously redundant // Program< Namespace< ExecutableCommand_ > > -> Program< ExecutableCommand_ > // aaa : done. The concept of `Namespace` has been removed - struct CommandsAggregatorCallback( Box< dyn Fn( &str, &Program< ExecutableCommand_ > ) > ); + struct CommandsAggregatorCallback( Box< dyn Fn( &str, &Program< VerifiedCommand > ) > ); impl fmt::Debug for CommandsAggregatorCallback { @@ -109,6 +109,10 @@ pub( crate ) mod private #[ perform( fn build() -> CommandsAggregator ) ] pub struct CommandsAggregator { + #[ setter( false ) ] + #[ default( Dictionary::default() ) ] + dictionary : Dictionary, + #[ default( Parser::former().form() ) ] parser : Parser, @@ -116,17 +120,17 @@ pub( crate ) mod private #[ default( Executor::former().form() ) ] executor : Executor, - help_generator : HelpGeneratorFn, - #[ default( HashSet::from([ HelpVariants::All ]) ) ] - help_variants : HashSet< HelpVariants >, + // help_generator : HelpGeneratorFn, + // #[ default( HashSet::from([ HelpVariants::All ]) ) ] + // help_variants : HashSet< HelpVariants >, // qqq : for Bohdan : should not have fields help_generator and help_variants // help_generator generateds VerifiedCommand(s) and stop to exist - #[ default( Verifier::former().form() ) ] - verifier : Verifier, + // #[ default( Verifier::former().form() ) ] + // verifier : Verifier, - #[ default( ExecutorConverter::former().form() ) ] - executor_converter : ExecutorConverter, + // #[ default( ExecutorConverter::former().form() ) ] + // executor_converter : ExecutorConverter, callback_fn : Option< CommandsAggregatorCallback >, } @@ -142,22 +146,12 @@ pub( crate ) mod private let on_end = | command : Command, super_former : Option< Self > | -> Self { let mut super_former = super_former.unwrap(); - if let Some( ref mut commands ) = super_former.container.verifier - { - commands.commands.entry( command.phrase.clone() ).or_default().push( command.clone() ); - } - else - { - super_former.container.verifier = Some( Verifier::former().command( command.clone() ).form() ); - } - if let Some( ref mut commands ) = super_former.container.executor_converter - { - commands.routines.insert( command.phrase, command.routine ); - } - else - { - super_former.container.executor_converter = Some( ExecutorConverter::former().routine( command.phrase, command.routine ).form() ); - } + let mut dictionary = super_former.container.dictionary.unwrap_or_default(); + + dictionary.register( command ); + + super_former.container.dictionary = Some( dictionary ); + super_former }; let former = CommandFormer::begin( Some( self ), on_end ); @@ -167,56 +161,56 @@ pub( crate ) mod private impl CommandsAggregatorFormer { - /// Setter for grammar - /// - /// Gets list of available commands - pub fn grammar< V >( mut self, commands : V ) -> Self - where - V : Into< Vec< Command > > - { - let verifier = Verifier::former() - .commands( commands ) - .form(); - self.container.verifier = Some( verifier ); - self - } + // /// Setter for grammar + // /// + // /// Gets list of available commands + // pub fn grammar< V >( mut self, commands : V ) -> Self + // where + // V : Into< Vec< Command > > + // { + // let verifier = Verifier::former() + // .commands( commands ) + // .form(); + // self.container.verifier = Some( verifier ); + // self + // } - /// Setter for executor - /// - /// Gets dictionary of routines( command name -> callback ) - pub fn executor< H >( mut self, routines : H ) -> Self - where - H : Into< HashMap< String, Routine > > - { - let executor = ExecutorConverter::former() - .routines( routines ) - .form(); + // /// Setter for executor + // /// + // /// Gets dictionary of routines( command name -> callback ) + // pub fn executor< H >( mut self, routines : H ) -> Self + // where + // H : Into< HashMap< String, Routine > > + // { + // let executor = ExecutorConverter::former() + // .routines( routines ) + // .form(); + // + // self.container.executor_converter = Some( executor ); + // self + // } - self.container.executor_converter = Some( executor ); - self - } - - /// Setter for help content generator - /// - /// ``` - /// use wca::CommandsAggregator; - /// - /// # fn main() -> Result< (), Box< dyn std::error::Error > > { - /// let ca = CommandsAggregator::former() - /// // ... - /// .help( | grammar, command | format!( "Replaced help content" ) ) - /// .perform(); - /// - /// ca.perform( ".help" )?; - /// # Ok( () ) } - /// ``` - pub fn help< HelpFunction >( mut self, func : HelpFunction ) -> Self - where - HelpFunction : Fn( &Verifier, Option< &Command > ) -> String + 'static - { - self.container.help_generator = Some( HelpGeneratorFn::new( func ) ); - self - } + // /// Setter for help content generator + // /// + // /// ``` + // /// use wca::CommandsAggregator; + // /// + // /// # fn main() -> Result< (), Box< dyn std::error::Error > > { + // /// let ca = CommandsAggregator::former() + // /// // ... + // /// .help( | grammar, command | format!( "Replaced help content" ) ) + // /// .perform(); + // /// + // /// ca.perform( ".help" )?; + // /// # Ok( () ) } + // /// ``` + // pub fn help< HelpFunction >( mut self, func : HelpFunction ) -> Self + // where + // HelpFunction : Fn( &Verifier, Option< &Command > ) -> String + 'static + // { + // self.container.help_generator = Some( HelpGeneratorFn::new( func ) ); + // self + // } // qqq : it is good access method, but formed structure should not have help_generator anymore /// Set callback function that will be executed after validation state @@ -236,7 +230,7 @@ pub( crate ) mod private /// ``` pub fn callback< Callback >( mut self, callback : Callback ) -> Self where - Callback : Fn( &str, &Program< ExecutableCommand_ > ) + 'static, + Callback : Fn( &str, &Program< VerifiedCommand > ) + 'static, { self.container.callback_fn = Some( CommandsAggregatorCallback( Box::new( callback ) ) ); self @@ -250,19 +244,19 @@ pub( crate ) mod private { let mut ca = self; - if ca.help_variants.contains( &HelpVariants::All ) - { - HelpVariants::All.generate( &ca.help_generator, &mut ca.verifier, &mut ca.executor_converter ); - } - else - { - for help in &ca.help_variants - { - help.generate( &ca.help_generator, &mut ca.verifier, &mut ca.executor_converter ); - } - } - - dot_command( &mut ca.verifier, &mut ca.executor_converter ); + // if ca.help_variants.contains( &HelpVariants::All ) + // { + // HelpVariants::All.generate( &ca.help_generator, &mut ca.dictionary ); + // } + // else + // { + // for help in &ca.help_variants + // { + // help.generate( &ca.help_generator, &mut ca.dictionary ); + // } + // } + // + // dot_command( &mut ca.dictionary ); ca } @@ -277,15 +271,15 @@ pub( crate ) mod private let Input( ref program ) = program.into_input(); let raw_program = self.parser.program( program ).map_err( | e | Error::Validation( ValidationError::Parser { input : program.to_string(), error : e } ) )?; - let grammar_program = self.verifier.to_program( raw_program ).map_err( | e | Error::Validation( ValidationError::Verifier( e ) ) )?; - let exec_program = self.executor_converter.to_program( grammar_program ).map_err( | e | Error::Validation( ValidationError::ExecutorConverter( e ) ) )?; + let grammar_program = Verifier::to_program( &self.dictionary, raw_program ).map_err( | e | Error::Validation( ValidationError::Verifier( e ) ) )?; + // let exec_program = self.executor_converter.to_program( grammar_program ).map_err( | e | Error::Validation( ValidationError::ExecutorConverter( e ) ) )?; if let Some( callback ) = &self.callback_fn { - callback.0( program, &exec_program ) + callback.0( program, &grammar_program ) } - self.executor.program( exec_program ).map_err( | e | Error::Execution( e ) ) + self.executor.program( &self.dictionary, grammar_program ).map_err( | e | Error::Execution( e ) ) } } } diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index 12348fd99f..8359eec4d9 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -48,11 +48,12 @@ pub( crate ) mod private /// Executes a program /// /// Setup runtimes for each namespace into program and run it with specified execution type - pub fn program( &self, program : Program< ExecutableCommand_ > ) -> Result< () > + pub fn program( &self, dictionary : &Dictionary, program : Program< VerifiedCommand > ) -> Result< () > { let context = self.context.clone(); let runtime = Runtime { + dictionary, context, pos : 0, namespace : program.commands, @@ -66,15 +67,16 @@ pub( crate ) mod private /// Executes a command /// /// Call command callback with context if it is necessary. - pub fn command( &self, command : ExecutableCommand_ ) -> Result< () > + pub fn command( &self, dictionary : &Dictionary, command : VerifiedCommand ) -> Result< () > { - _exec_command( command, self.context.clone() ) + let routine = dictionary.command( &command.phrase ).unwrap().routine.clone(); + _exec_command( command, routine, self.context.clone() ) } // qqq : for Bohdan : probably redundant // aaa : removed `parallel_execution_loop` - fn sequential_execution_loop( mut runtime : Runtime ) -> Result< () > + fn sequential_execution_loop( mut runtime : Runtime< '_ > ) -> Result< () > { while !runtime.is_finished() { diff --git a/module/move/wca/src/ca/executor/mod.rs b/module/move/wca/src/ca/executor/mod.rs index fb73da2acb..95c0208e12 100644 --- a/module/move/wca/src/ca/executor/mod.rs +++ b/module/move/wca/src/ca/executor/mod.rs @@ -5,12 +5,12 @@ crate::mod_interface! layer executor; /// Represents the state of the program's runtime layer runtime; - /// Converts from `VerifiedCommand` to `ExecutableCommand_` - layer converter; + // /// Converts from `VerifiedCommand` to `ExecutableCommand_` + // layer converter; /// Container for contexts values layer context; - /// `ExecutableCommand_` representation - layer command; + // /// `ExecutableCommand_` representation + // layer command; /// Command callback representation layer routine; diff --git a/module/move/wca/src/ca/executor/runtime.rs b/module/move/wca/src/ca/executor/runtime.rs index 57ad91eafc..ca789c6935 100644 --- a/module/move/wca/src/ca/executor/runtime.rs +++ b/module/move/wca/src/ca/executor/runtime.rs @@ -46,20 +46,21 @@ pub( crate ) mod private /// assert!( runtime.is_finished() ); /// ``` #[ derive( Debug, Clone ) ] - pub struct Runtime + pub struct Runtime< 'a > { + pub dictionary : &'a Dictionary, /// context for current runtime pub context : Context, /// current execution position pub pos : usize, /// namespace which must be executed - pub namespace : Vec< ExecutableCommand_ >, // qqq : for Bohdan : use VerifiedCommand + pub namespace : Vec< VerifiedCommand >, // qqq : for Bohdan : use VerifiedCommand } // qqq : for Bohdan : why both Runtime and RuntimeState exist? probably one should removed // qqq : for Bohdan : why both Runtime and Context exist? What about incapsulating Context into Runtime maybe // qqq : for Bohdan : why both Runtime and Executor exist? rid off of Executor. Incapsulating Executor into Runtime. - impl Runtime + impl Runtime< '_ > { /// returns true if execution position at the end pub fn is_finished( &self ) -> bool @@ -76,7 +77,8 @@ pub( crate ) mod private .ok_or_else( || err!( "No command here. Current execution pos was `{}`", self.pos ) ) .and_then( | cmd | { - _exec_command( cmd.clone(), self.context.clone() ) + let routine = self.dictionary.command( &cmd.phrase ).unwrap().routine.clone(); + _exec_command( cmd.clone(), routine, self.context.clone() ) }) } } @@ -84,9 +86,9 @@ pub( crate ) mod private // qqq : for Bohdan : _exec_command probably should be method of Runtime. // qqq : for Bohdan : Accept reference instead of copy. /// executes a command - pub fn _exec_command( command : ExecutableCommand_, ctx : Context ) -> Result< () > + pub fn _exec_command( command : VerifiedCommand, routine : Routine, ctx : Context ) -> Result< () > { - match command.routine + match routine { Routine::WithoutContext( routine ) => routine( ( Args( command.subjects ), Props( command.properties ) )), Routine::WithContext( routine ) => routine( ( Args( command.subjects ), Props( command.properties ) ), ctx ), diff --git a/module/move/wca/src/ca/grammar/dictionary.rs b/module/move/wca/src/ca/grammar/dictionary.rs index 48ed218410..3db0f39130 100644 --- a/module/move/wca/src/ca/grammar/dictionary.rs +++ b/module/move/wca/src/ca/grammar/dictionary.rs @@ -4,11 +4,70 @@ pub( crate ) mod private use { Command }; use std::collections::HashMap; + use former::Former; + + // qqq : `Former` does not handle this situation well + + // /// A collection of commands. + // /// + // /// This structure holds a hashmap of commands where each command is mapped to its name. + // #[ derive( Debug, Former ) ] + // pub struct Dictionary( HashMap< String, Command > ); /// A collection of commands. /// /// This structure holds a hashmap of commands where each command is mapped to its name. - pub struct Dictionary( HashMap< String, Command > ); + #[ derive( Debug, Default, Former ) ] + pub struct Dictionary + { + #[ setter( false ) ] + pub( crate ) commands : HashMap< String, Command >, + } + + // qqq : IDK how to integrate it into the `CommandsAggregatorFormer` + // + impl DictionaryFormer + { + pub fn command( mut self, command : Command ) -> Self + { + let mut commands = self.container.commands.unwrap_or_default(); + commands.extend([( command.phrase.clone(), command )]); + self.container.commands = Some( commands ); + + self + } + } + + impl Dictionary + { + /// Registers a command into the command list. + /// + /// # Arguments + /// + /// * `command` - The command to be registered. + pub fn register( &mut self, command : Command ) + { + self.commands.insert( command.phrase.clone(), command ); + } + + /// Retrieves the command with the specified `name` from the `commands` hashmap. + /// + /// # Arguments + /// + /// * `name` - A reference to the name of the command to retrieve. + /// + /// # Returns + /// + /// An `Option` containing a reference to the command with the specified `name`, if it exists. + /// Returns `None` if no command with the specified `name` is found. + pub fn command< Name >( &self, name : &Name ) -> Option< &Command > + where + String : std::borrow::Borrow< Name >, + Name : std::hash::Hash + Eq, + { + self.commands.get( name ) + } + } } // diff --git a/module/move/wca/src/ca/mod.rs b/module/move/wca/src/ca/mod.rs index d3de3f3696..b69db8e8c1 100644 --- a/module/move/wca/src/ca/mod.rs +++ b/module/move/wca/src/ca/mod.rs @@ -16,15 +16,15 @@ crate::mod_interface! /// User input layer input; - /// The missing batteries of WCA. - layer facade; + // /// The missing batteries of WCA. + // layer facade; /// Responsible for aggregating all commands that the user defines, and for parsing and executing them layer aggregator; - /// Helper commands - layer help; - /// - - layer formatter; + // /// Helper commands + // layer help; + // /// - + // layer formatter; // qqq : for Bohdan : write concise documentations } diff --git a/module/move/wca/src/ca/verifier/command.rs b/module/move/wca/src/ca/verifier/command.rs index 3c142d9bf9..bd59173212 100644 --- a/module/move/wca/src/ca/verifier/command.rs +++ b/module/move/wca/src/ca/verifier/command.rs @@ -24,7 +24,7 @@ pub( crate ) mod private /// /// In the above example, a `VerifiedCommand` instance is created with the name "command", a single subject "subject_value", and one property "prop_name" with a typed values. /// - #[ derive( Debug ) ] + #[ derive( Debug, Clone ) ] pub struct VerifiedCommand { /// Phrase descriptor for command. diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index eb2897eb41..16b98dcc38 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -38,68 +38,68 @@ pub( crate ) mod private /// # } /// ``` #[ derive( Debug, Clone ) ] - #[ derive( Former ) ] - pub struct Verifier - { - // TODO: Make getters - /// all available commands - #[ setter( false ) ] - pub commands : HashMap< String, Vec< Command > >, // qqq : for Bohdan : <- introduce Dictionary for HashMap< String, Vec< Command > > - } - - impl VerifierFormer - { - /// Insert a command to the commands list - pub fn command( mut self, command : Command ) -> Self - { - let mut commands = self.container.commands.unwrap_or_default(); - - let command_variants = commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); - command_variants.push( command ); - - self.container.commands = Some( commands ); - self - } + // #[ derive( Former ) ] + pub struct Verifier; + // { + // // TODO: Make getters + // /// all available commands + // #[ setter( false ) ] + // pub commands : &'a Dictionary, // qqq : for Bohdan : <- introduce Dictionary for HashMap< String, Vec< Command > > + // } - /// Expands the list of commands with received commands - pub fn commands< V >( mut self, commands : V ) -> Self - where - V : Into< Vec< Command > > - { - let mut self_commands = self.container.commands.unwrap_or_default(); - - for command in commands.into() - { - let command_variants = self_commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); - command_variants.push( command ); - } - - self.container.commands = Some( self_commands ); - self - } - } + // impl VerifierFormer + // { + // /// Insert a command to the commands list + // pub fn command( mut self, command : Command ) -> Self + // { + // let mut commands = self.container.commands.unwrap_or_default(); + // + // let command_variants = commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); + // command_variants.push( command ); + // + // self.container.commands = Some( commands ); + // self + // } + // + // /// Expands the list of commands with received commands + // pub fn commands< V >( mut self, commands : V ) -> Self + // where + // V : Into< Vec< Command > > + // { + // let mut self_commands = self.container.commands.unwrap_or_default(); + // + // for command in commands.into() + // { + // let command_variants = self_commands.entry( command.phrase.to_owned() ).or_insert_with( Vec::new ); + // command_variants.push( command ); + // } + // + // self.container.commands = Some( self_commands ); + // self + // } + // } impl Verifier { /// Converts raw program to grammatically correct /// /// Converts all namespaces into it with `to_namespace` method. - pub fn to_program( &self, raw_program : Program< ParsedCommand > ) + pub fn to_program( dictionary : &Dictionary, raw_program : Program< ParsedCommand > ) -> Result< Program< VerifiedCommand > > { let commands = raw_program.commands .into_iter() - .map( | n | self.to_command( n ) ) + .map( | n | Self::to_command( dictionary, n ) ) .collect::< Result< Vec< VerifiedCommand > > >()?; Ok( Program { commands } ) } #[ cfg( feature = "on_unknown_suggest" ) ] - fn suggest_command( &self, user_input: &str ) -> Option< &str > + fn suggest_command( dictionary : &Dictionary, user_input: &str ) -> Option< &str > { let jaro = eddie::JaroWinkler::new(); - let sim = self + let sim = dictionary .commands .iter() .map( |( name, c )| ( jaro.similarity( name, user_input ), c ) ) @@ -108,7 +108,7 @@ pub( crate ) mod private { if sim > 0.0 { - let phrase = &variant[ 0 ].phrase; + let phrase = &variant.phrase; return Some( phrase ); } } @@ -118,7 +118,7 @@ pub( crate ) mod private fn find_variant< 'a > ( - variants: &'a [ Command ], + variants: &'a Command, raw_command : &ParsedCommand, ) -> Option< &'a Command > { @@ -131,7 +131,7 @@ pub( crate ) mod private properties_aliases, .. } - in variants + in [ variants ] { let raw_subjects_count = raw_command.subjects.len(); let expected_subjects_count = subjects.len(); @@ -236,15 +236,15 @@ pub( crate ) mod private /// Converts raw command to grammatically correct /// /// Make sure that this command is described in the grammar and matches it(command itself and all it options too). - pub fn to_command( &self, raw_command : ParsedCommand ) -> Result< VerifiedCommand > + pub fn to_command( dictionary : &Dictionary, raw_command : ParsedCommand ) -> Result< VerifiedCommand > { - let variants = self.commands.get( &raw_command.name ) + let variants = dictionary.command( &raw_command.name ) .ok_or_else::< error::for_app::Error, _ > ( || { #[ cfg( feature = "on_unknown_suggest" ) ] - if let Some( phrase ) = self.suggest_command( &raw_command.name ) + if let Some( phrase ) = Self::suggest_command( dictionary, &raw_command.name ) { return err!( "Command not found. Maybe you mean `.{}`?", phrase ) } err!( "Command not found. Please use `.` command to see the list of available commands." ) } @@ -256,7 +256,8 @@ pub( crate ) mod private ( "`{}` command with specified subjects not found. Available variants `{:#?}`", &raw_command.name, - variants.iter() + [ variants ] + .into_iter() .map ( | x |