mod private
{
use crate :: *;
use ca ::help :: { HelpGeneratorOptions, generate_help_content, LevelOfDetail };
use verifier ::VerifiedCommand;
use parser ::Program;
use grammar ::Dictionary;
use error_tools ::untyped ::Result;
use error_tools ::dependency ::thiserror;
use executor :: { Routine, Context };
#[ derive( Debug, error_tools ::typed ::Error ) ]
pub enum CommandError
{
#[ error( "Internal command: `.{}` failed with: {}", command.phrase, error ) ]
Internal { command: VerifiedCommand, error: InternalCommandError },
#[ error( "Command: `.{}` failed with: {}", command.phrase, error ) ]
User { command: VerifiedCommand, error: error_tools ::error ::untyped ::Error },
}
#[ derive( Debug, former ::Former ) ]
pub struct Executor
{
#[ former( default = Context ::default() ) ]
pub context: Context,
}
impl Executor
{
pub fn program( &self, dictionary: &Dictionary, program: Program< VerifiedCommand > )
-> Result< (), Box< CommandError > >
{
for command in program.commands
{
self.command( dictionary, command )?;
}
Ok( () )
}
pub fn command( &self, dictionary: &Dictionary, command: VerifiedCommand )
-> Result< (), Box< CommandError > >
{
if command.internal_command
{
exec_internal_command( dictionary, command.clone() )
.map_err( | error | Box ::new( CommandError ::Internal { command, error } ) )
}
else
{
let routine = dictionary.command( &command.phrase ).unwrap().routine.clone();
exec_command( command.clone(), routine, self.context.clone() )
.map_err( | error | Box ::new( CommandError ::User { command, error } ) )
}
}
}
fn exec_command( command: VerifiedCommand, routine: Routine, ctx: Context )
-> error_tools ::error ::untyped ::Result< () >
{
match routine
{
Routine ::WithoutContext( routine ) => routine( command ),
Routine ::WithContext( routine ) => routine( ctx, command ),
}
}
#[ derive( Debug, error_tools ::typed ::Error ) ]
pub enum InternalCommandError
{
#[ error( "Encountered an unrecognized internal command: `.{user_input}`." ) ]
UnknownInternalCommand { user_input: String },
#[ error( "Not found command that starts with `.{user_input}`." ) ]
CommandNotFound { user_input: String },
}
#[ allow( clippy ::needless_pass_by_value ) ]
fn exec_internal_command( dictionary: &Dictionary, command: VerifiedCommand )
-> Result< (), InternalCommandError >
{
match command.phrase.as_str()
{
"." =>
{
let generator_args = HelpGeneratorOptions ::former()
.command_prefix( "." )
.form();
let content = generate_help_content( dictionary, generator_args );
println!( "{content}" );
}
".?" =>
{
let generator_args = HelpGeneratorOptions ::former()
.description_detailing( LevelOfDetail ::Simple )
.subject_detailing( LevelOfDetail ::Simple )
.property_detailing( LevelOfDetail ::Simple )
.form();
let content = generate_help_content( dictionary, generator_args );
println!( "{content}" );
}
name if name.ends_with( '.' ) =>
{
let name = name.strip_suffix( '.' ).unwrap();
let commands = dictionary.search( name.strip_prefix( '.' ).unwrap_or( name ) );
if commands.is_empty()
{
return Err( InternalCommandError ::CommandNotFound { user_input: name.into() } );
}
let generator_args = HelpGeneratorOptions ::former()
.command_prefix( "." )
.for_commands( commands )
.form();
let content = generate_help_content( dictionary, generator_args );
println!( "{content}" );
}
name if name.ends_with( ".?" ) =>
{
let name = name.strip_suffix( ".?" ).unwrap();
let command = dictionary.command( &name.strip_prefix( '.' ).unwrap_or( name ).to_string() );
if let Some( command ) = command
{
let generator_args = HelpGeneratorOptions ::former()
.for_commands([ command ])
.description_detailing( LevelOfDetail ::Detailed )
.subject_detailing( LevelOfDetail ::Simple )
.property_detailing( LevelOfDetail ::Simple )
.with_footer( true )
.form();
let content = generate_help_content( dictionary, generator_args );
println!( "{content}" );
}
else
{
return Err( InternalCommandError ::CommandNotFound { user_input: name.into() } );
}
}
unexpected => return Err( InternalCommandError ::UnknownInternalCommand { user_input: unexpected.into() }),
}
Ok( () )
}
}
crate ::mod_interface!
{
exposed use Executor;
}