#[ allow( clippy ::std_instead_of_alloc, clippy ::std_instead_of_core ) ]
mod private
{
use crate :: *;
use ca ::
{
Executor,
grammar ::command ::
{
CommandFormer,
CommandAsSubformer,
CommandAsSubformerEnd,
CommandFormerStorage
},
help :: { HelpGeneratorFn, HelpGeneratorOptions, HelpVariants },
};
use verifier :: { Verifier, VerificationError, VerifiedCommand };
use parser :: { Program, Parser, ParserError };
use grammar ::Dictionary;
use executor ::Context;
use input :: { Input, IntoInput };
use error_tools ::dependency ::thiserror;
use std ::
{
fmt,
collections ::HashSet
};
use former ::StoragePreform;
use error_tools ::untyped ::Error as wError;
use iter_tools ::Itertools;
#[ derive( Debug, Default, Clone, Copy, Eq, PartialOrd, PartialEq ) ]
pub enum Order
{
#[ default ]
Nature,
Lexicography,
}
#[ derive( error_tools ::Error, Debug ) ]
pub enum ValidationError
{
#[ error( "The following input is not recognized: `{input}`.\nDetails: {error}" ) ]
Parser
{
input: String,
error: ParserError,
},
#[ error( "Can not identify a command.\nDetails: {0}" ) ]
Verifier( VerificationError ),
#[ error( "Can not find a routine for a command.\nDetails: {0}" ) ]
ExecutorConverter( wError ),
}
#[ derive( error_tools ::Error, Debug ) ]
pub enum Error
{
#[ error( "Validation error\n{0}" ) ]
Validation( ValidationError ),
#[ error( "Execution failed\n{0:?}" ) ]
Execution( wError ),
}
#[ allow( clippy ::type_complexity ) ]
struct CommandsAggregatorCallback( Box< dyn Fn( &str, &Program< VerifiedCommand > ) > );
impl fmt ::Debug for CommandsAggregatorCallback
{
fn fmt( &self, f: &mut fmt ::Formatter< '_ > ) -> fmt ::Result
{
f.debug_struct( "CommandsAggregatorCallback" ).finish_non_exhaustive()
}
}
#[ derive( Debug ) ]
#[ derive( former ::Former ) ]
#[ storage_fields( help_generator: HelpGeneratorFn, help_variants: HashSet< HelpVariants >, order: Order ) ]
#[ mutator( custom ) ]
pub struct CommandsAggregator
{
#[ former( default = Dictionary ::default() ) ]
dictionary: Dictionary,
#[ former( default = Parser ) ]
parser: Parser,
#[ scalar( setter = false ) ]
#[ former( default = Executor ::former().form() ) ]
executor: Executor,
#[ former( default = Verifier ) ]
verifier: Verifier,
callback_fn: Option< CommandsAggregatorCallback >,
}
impl< Context, Formed > former ::FormerMutator for CommandsAggregatorFormerDefinitionTypes< Context, Formed >
{
fn form_mutation( storage: &mut Self ::Storage, _context: &mut Option< Self ::Context > )
{
let ca = storage;
let dictionary = ca.dictionary.get_or_insert_with( Dictionary ::default );
dictionary.order = ca.order.unwrap_or_default();
let help_generator = core ::mem ::take( &mut ca.help_generator ).unwrap_or_default();
let help_variants = core ::mem ::take( &mut ca.help_variants ).unwrap_or_else( || HashSet ::from( [ HelpVariants ::All ] ) );
if help_variants.contains( &HelpVariants ::All )
{
HelpVariants ::All.generate( &help_generator, dictionary, ca.order.unwrap_or_default() );
}
else
{
for help in help_variants.iter().sorted()
{
help.generate( &help_generator, dictionary, ca.order.unwrap_or_default() );
}
}
}
}
impl< Definition > CommandsAggregatorFormer< Definition >
where
Definition: former ::FormerDefinition< Storage = < CommandsAggregator as former ::EntityToStorage > ::Storage >,
{
pub fn command< IntoName >( self, name: IntoName ) -> CommandAsSubformer< Self, impl CommandAsSubformerEnd< Self > >
where
IntoName: Into< String >,
{
let name = name.into();
let on_end = | command: CommandFormerStorage, super_former: Option< Self > | -> Self
{
let mut super_former = super_former.unwrap();
let mut dictionary = super_former.storage.dictionary.unwrap_or_default();
dictionary.register( command.preform() );
super_former.storage.dictionary = Some( dictionary );
super_former
};
let former = CommandFormer ::begin( None, Some( self ), on_end );
former.phrase( name )
}
}
impl CommandsAggregatorFormer
{
#[ must_use ]
pub fn with_context< T >( mut self, value: T ) -> Self
where
T: Sync + Send + 'static,
{
let mut executor = self.storage.executor.unwrap_or_else( || Executor ::former().form() );
executor.context = Context ::new( value );
self.storage.executor = Some( executor );
self
}
#[ must_use ]
pub fn help< HelpFunction >( mut self, func: HelpFunction ) -> Self
where
HelpFunction: Fn( &Dictionary, HelpGeneratorOptions< '_ > ) -> String + 'static
{
self.storage.help_generator = Some( HelpGeneratorFn ::new( func ) );
self
}
#[ must_use ]
pub fn callback< Callback >( mut self, callback: Callback ) -> Self
where
Callback: Fn( &str, &Program< VerifiedCommand > ) + 'static,
{
self.storage.callback_fn = Some( CommandsAggregatorCallback( Box ::new( callback ) ) );
self
}
}
impl CommandsAggregator
{
pub fn perform< S >( &self, program: S ) -> Result< (), Error >
where
S: IntoInput
{
let Input( ref program ) = program.into_input();
let raw_program = self.parser.parse( program ).map_err( | e |
{
Error ::Validation( ValidationError ::Parser { input: format!( "{program:?}" ), error: e } )
})?;
let grammar_program = self.verifier.to_program( &self.dictionary, raw_program ).map_err( | e |
{
Error ::Validation( ValidationError ::Verifier( e ) )
})?;
if let Some( callback ) = &self.callback_fn
{
callback.0( &program.join( " " ), &grammar_program );
}
self.executor.program( &self.dictionary, grammar_program ).map_err( | e | Error ::Execution( e.into() ) )
}
}
}
crate ::mod_interface!
{
exposed use CommandsAggregator;
orphan use CommandsAggregatorFormer;
exposed use Error;
exposed use ValidationError;
exposed use Order;
}