pub mod decode; pub mod encode; pub mod import_dict; pub mod list_dicts; use crate::config::AppConfig; use crate::container::Container; use anyhow::Result; use async_trait::async_trait; use clap::Subcommand; use clap::{Args as ClapArgs, Parser}; use config::ConfigBuilder; use config::builder::DefaultState; use std::path::PathBuf; #[derive(Subcommand, Debug, Clone)] pub enum Command { /// Decode a word using given system Decode(decode::DecodeCmd), /// Encode a number using given system Encode(encode::EncodeCmd), /// Import dictionary ImportDict(import_dict::ImportDictCmd), /// List all dictionaries ListDicts(list_dicts::ListDictsCmd), } impl Command { pub fn into_app_command(self) -> Box { match self { Command::Decode(cmd) => Box::new(cmd), Command::Encode(cmd) => Box::new(cmd), Command::ImportDict(cmd) => Box::new(cmd), Command::ListDicts(cmd) => Box::new(cmd), } } } // pub fn resolve_command(command: &Command) -> &dyn AppCommand { // match command { // Command::Decode(app_cmd) => app_cmd, // Command::Encode(app_cmd) => app_cmd, // Command::ImportDict(app_cmd) => app_cmd, // } // } // pub fn resolve_command_box(command: Command) -> Box { // match command { // Command::Decode(cmd) => Box::new(cmd), // Command::Encode(cmd) => Box::new(cmd), // Command::ImportDict(cmd) => Box::new(cmd), // } // } #[derive(Parser, Debug)] #[command(author, version, about)] pub struct CliArgs { #[command(flatten)] pub global: GlobalArgs, #[command(subcommand)] pub command: Command, } #[derive(ClapArgs, Debug)] pub struct GlobalArgs { /// Path to config file #[arg(short, long, default_value = "config.toml")] pub config: PathBuf, #[arg(long, help = defaults::HELP_LOG)] pub log_level: Option, } pub trait Configurable { fn apply_defaults( &self, builder: ConfigBuilder, ) -> Result>; fn apply_overrides( &self, builder: ConfigBuilder, ) -> Result>; } #[async_trait] pub trait Executable { async fn execute(&self, config: &AppConfig, container: &Container) -> Result<()>; } // AppCommand must be dyn-compatible. Configurable is already dyn-compatible. // Executable is dyn-compatible because of #[async_trait]. pub trait AppCommand: Configurable + Executable {} impl AppCommand for T {} mod defaults { use const_format::formatcp; pub const LOG_LEVEL: &str = "info"; pub const HELP_LOG: &str = formatcp!("Override Log Level [default: {}]", LOG_LEVEL); }