From 307971e600870b8089f85e51b850e7db1c40d2d9 Mon Sep 17 00:00:00 2001 From: chodak166 Date: Thu, 1 Jan 2026 19:18:45 +0100 Subject: [PATCH] WIP: refactor --- app/Cargo.toml | 1 + app/src/app.rs | 46 +++---------- app/src/config.rs | 57 ++++------------ app/src/main.rs | 2 + lib/src/core/traits.rs | 4 +- lib/src/presentation/cli.rs | 8 ++- lib/src/presentation/cli/cli_args.rs | 98 +++++++++++++++++++++++++++- lib/src/presentation/cli/defaults.rs | 41 +----------- 8 files changed, 129 insertions(+), 128 deletions(-) diff --git a/app/Cargo.toml b/app/Cargo.toml index 5a8838f..7f87eb2 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -13,6 +13,7 @@ tokio = { version = "1.48", features = ["full"] } anyhow = "1.0" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } +async-trait = "0.1" # Configuration & Inputs serde = { version = "1.0", features = ["derive"] } diff --git a/app/src/app.rs b/app/src/app.rs index 6eaf573..e6fabf6 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -1,10 +1,10 @@ use crate::config::AppConfig; use crate::container::Container; +use crate::handlers::resolve_command; use anyhow::Result; -use applib::cli::{CliArgs, Command, commands}; +use applib::cli::{CliArgs, Command}; use clap::Parser; -use tokio::signal; -use tracing::{debug, info, warn}; +use tracing::debug; pub struct Application { config: AppConfig, @@ -16,7 +16,10 @@ impl Application { pub async fn build() -> Result { let args = CliArgs::parse(); - let config = AppConfig::build(&args.global, &args.command)?; + // Resolve the command once to get the trait object for configuration + let handler = resolve_command(&args.command); + + let config = AppConfig::build(&args.global, handler)?; tracing_subscriber::fmt() .compact() @@ -36,38 +39,7 @@ impl Application { } pub async fn run(self) -> Result<()> { - match self.command { - Command::Server(_) => { - let config = self.config.server.expect("Server config not set"); - commands::server::run(config, Self::wait_for_shutdown_signal()).await; - } - Command::Decode(_) => { - let config = self.config.decoder.expect("Decoder config not set"); - commands::decode::run(config).await; - } - Command::Encode(_) => { - let config = self.config.encoder.expect("Encoder config not set"); - let repo = self.container.create_dict_repo("demo_pl").await?; - - commands::encode::run(config, repo.as_ref()).await; - } - Command::ImportDict(_) => { - let config = self.config.import_dict.expect("ImportDict config not set"); - let importer = self.container.create_dict_importer(&config.name).await?; - commands::import_dict::run(config, importer).await?; - } - } - Ok(()) - } - - async fn wait_for_shutdown_signal() { - match signal::ctrl_c().await { - Ok(()) => { - info!("Received shutdown signal (SIGINT/SIGTERM)"); - } - Err(err) => { - warn!("Failed to listen for shutdown signal: {}", err); - } - } + let handler = resolve_command(&self.command); + handler.execute(&self.config, &self.container).await } } diff --git a/app/src/config.rs b/app/src/config.rs index 5c5e46d..2470884 100644 --- a/app/src/config.rs +++ b/app/src/config.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use config::{Config, Environment, File}; use serde::Deserialize; -use applib::cli::{Command, GlobalArgs, defaults::set_command_defaults}; +use applib::cli::{ConfigurableCommand, GlobalArgs}; use applib::config::*; #[derive(Debug, Deserialize, Clone)] @@ -19,11 +19,11 @@ pub struct AppConfig { } impl AppConfig { - pub fn build(args: &GlobalArgs, command: &Command) -> Result { + pub fn build(args: &GlobalArgs, handler: &dyn ConfigurableCommand) -> Result { let mut builder = Config::builder(); - // Command-specific defaults - builder = set_command_defaults(builder, command)?; + // Command-specific defaults via Trait + builder = handler.apply_defaults(builder)?; // File Layer let config_path = &args.config; @@ -34,8 +34,13 @@ impl AppConfig { // Environment Layer (APP_SERVER_PORT) builder = builder.add_source(Environment::with_prefix("APP").separator("_")); - // CLI Overrides Layer - builder = apply_cli_overrides(builder, args, command)?; + // Global log level override + if let Some(ref level) = args.log_level { + builder = builder.set_override("log_level", level.clone())?; + } + + // Command-specific overrides via Trait + builder = handler.apply_overrides(builder)?; builder .build() @@ -44,43 +49,3 @@ impl AppConfig { .context("Failed to deserialize Config") } } - -fn apply_cli_overrides( - builder: config::ConfigBuilder, - args: &GlobalArgs, - command: &Command, -) -> Result> { - let mut builder = builder; - - // Global log level override - if let Some(ref level) = args.log_level { - builder = builder.set_override("log_level", level.clone())?; - } - - // Command-specific overrides - match command { - Command::Server(cmd_args) => { - if let Some(port) = cmd_args.port { - builder = builder.set_override("server.port", port)?; - } - } - Command::Decode(cmd_args) => { - if let Some(name) = &cmd_args.system { - builder = builder.set_override("decoder.system", name.as_str())?; - } - builder = builder.set_override("decoder.input", cmd_args.input.clone())?; - } - Command::Encode(cmd_args) => { - if let Some(name) = &cmd_args.system { - builder = builder.set_override("encoder.system", name.as_str())?; - } - builder = builder.set_override("encoder.input", cmd_args.input.clone())?; - } - Command::ImportDict(cmd_args) => { - builder = builder.set_override("import_dict.name", cmd_args.name.clone())?; - builder = builder.set_override("import_dict.path", cmd_args.path.clone())?; - } - } - - Ok(builder) -} diff --git a/app/src/main.rs b/app/src/main.rs index 406ee88..ab9b63f 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,6 +1,8 @@ mod app; mod config; mod container; +mod handlers; +mod traits; use anyhow::Result; use app::Application; diff --git a/lib/src/core/traits.rs b/lib/src/core/traits.rs index 1e1218c..e0093f8 100644 --- a/lib/src/core/traits.rs +++ b/lib/src/core/traits.rs @@ -6,11 +6,11 @@ use crate::core::errors::CodecError; use super::entities::{DecodedValue, Dict, DictEntry}; use super::errors::RepositoryError; -pub trait SystemDecoder { +pub trait SystemDecoder: Send + Sync { fn decode(&self, word: &str) -> Result; } -pub trait SystemEncoder { +pub trait SystemEncoder: Send + Sync { fn initialize(&self) -> Result<(), CodecError>; fn encode(&self, word: &str) -> Result; } diff --git a/lib/src/presentation/cli.rs b/lib/src/presentation/cli.rs index 846d92d..f89bbcd 100644 --- a/lib/src/presentation/cli.rs +++ b/lib/src/presentation/cli.rs @@ -1,7 +1,9 @@ pub mod cli_args; pub mod commands; pub mod defaults; +pub mod traits; -pub use self::cli_args::CliArgs; -pub use self::cli_args::Command; -pub use self::cli_args::GlobalArgs; +pub use self::cli_args::{ + CliArgs, Command, DecodeArgs, EncodeArgs, GlobalArgs, ImportDictArgs, ServerArgs, +}; +pub use self::traits::ConfigurableCommand; diff --git a/lib/src/presentation/cli/cli_args.rs b/lib/src/presentation/cli/cli_args.rs index e7994df..d9c5e5c 100644 --- a/lib/src/presentation/cli/cli_args.rs +++ b/lib/src/presentation/cli/cli_args.rs @@ -1,5 +1,8 @@ -use crate::cli::defaults; +use crate::cli::{ConfigurableCommand, defaults}; +use anyhow::Result; use clap::{Args as ClapArgs, Parser, Subcommand}; +use config::ConfigBuilder; +use config::builder::DefaultState; use std::path::PathBuf; #[derive(Parser, Debug)] @@ -43,6 +46,29 @@ pub struct ServerArgs { pub port: Option, } +impl ConfigurableCommand for ServerArgs { + fn apply_defaults( + &self, + builder: ConfigBuilder, + ) -> Result> { + builder + .set_default("server.host", defaults::HOST)? + .set_default("server.port", defaults::PORT) + .map_err(Into::into) + } + + fn apply_overrides( + &self, + builder: ConfigBuilder, + ) -> Result> { + let mut builder = builder; + if let Some(port) = self.port { + builder = builder.set_override("server.port", port)?; + } + Ok(builder) + } +} + #[derive(ClapArgs, Debug, Clone)] pub struct DecodeArgs { #[arg(long, help = defaults::HELP_DEC_SYSTEM)] @@ -52,6 +78,30 @@ pub struct DecodeArgs { pub input: String, } +impl ConfigurableCommand for DecodeArgs { + fn apply_defaults( + &self, + builder: ConfigBuilder, + ) -> Result> { + builder + .set_default("decoder.system", defaults::SYSTEM_NAME)? + .set_default("decoder.input", "") + .map_err(Into::into) + } + + fn apply_overrides( + &self, + builder: ConfigBuilder, + ) -> Result> { + let mut builder = builder; + if let Some(ref system) = self.system { + builder = builder.set_override("decoder.system", system.clone())?; + } + builder = builder.set_override("decoder.input", self.input.clone())?; + Ok(builder) + } +} + #[derive(ClapArgs, Debug, Clone)] pub struct EncodeArgs { #[arg(long, help = defaults::HELP_ENC_SYSTEM)] @@ -61,6 +111,30 @@ pub struct EncodeArgs { pub input: String, } +impl ConfigurableCommand for EncodeArgs { + fn apply_defaults( + &self, + builder: ConfigBuilder, + ) -> Result> { + builder + .set_default("encoder.system", defaults::SYSTEM_NAME)? + .set_default("encoder.input", "") + .map_err(Into::into) + } + + fn apply_overrides( + &self, + builder: ConfigBuilder, + ) -> Result> { + let mut builder = builder; + if let Some(ref system) = self.system { + builder = builder.set_override("encoder.system", system.clone())?; + } + builder = builder.set_override("encoder.input", self.input.clone())?; + Ok(builder) + } +} + #[derive(ClapArgs, Debug, Clone)] pub struct ImportDictArgs { #[arg(long, help = defaults::HELP_IMPORT_DICT_NAME)] @@ -69,3 +143,25 @@ pub struct ImportDictArgs { #[arg(long, help = defaults::HELP_IMPORT_DICT_INPUT)] pub path: String, } + +impl ConfigurableCommand for ImportDictArgs { + fn apply_defaults( + &self, + builder: ConfigBuilder, + ) -> Result> { + builder + .set_default("import_dict.name", defaults::IMPORT_DICT_NAME)? + .set_default("import_dict.path", defaults::IMPORT_DICT_PATH) + .map_err(Into::into) + } + + fn apply_overrides( + &self, + builder: ConfigBuilder, + ) -> Result> { + builder + .set_override("import_dict.name", self.name.clone())? + .set_override("import_dict.path", self.path.clone()) + .map_err(Into::into) + } +} diff --git a/lib/src/presentation/cli/defaults.rs b/lib/src/presentation/cli/defaults.rs index 5048cc3..aee0e16 100644 --- a/lib/src/presentation/cli/defaults.rs +++ b/lib/src/presentation/cli/defaults.rs @@ -1,10 +1,3 @@ -use anyhow::Result; -pub use config::ConfigBuilder; - -use const_format::formatcp; - -use crate::cli::Command; - pub const HOST: &str = "127.0.0.1"; pub const PORT: u16 = 8080; pub const LOG_LEVEL: &str = "info"; @@ -12,6 +5,8 @@ pub const SYSTEM_NAME: &str = "major_pl"; pub const IMPORT_DICT_NAME: &str = ""; pub const IMPORT_DICT_PATH: &str = ""; +use const_format::formatcp; + pub const HELP_PORT: &str = formatcp!("Override Port [default: {}]", PORT); pub const HELP_LOG: &str = formatcp!("Override Log Level [default: {}]", LOG_LEVEL); pub const HELP_DEC_SYSTEM: &str = formatcp!("System to use [default: {}]", SYSTEM_NAME); @@ -20,35 +15,3 @@ pub const HELP_ENC_SYSTEM: &str = formatcp!("System to use [default: {}]", SYSTE pub const HELP_ENC_INPUT: &str = formatcp!("Number to encode"); pub const HELP_IMPORT_DICT_NAME: &str = formatcp!("Dictionary name"); pub const HELP_IMPORT_DICT_INPUT: &str = formatcp!("Dictionary file path"); - -pub fn set_command_defaults( - builder: ConfigBuilder, - command: &Command, -) -> Result> { - let mut builder = builder.set_default("log_level", LOG_LEVEL)?; - - match command { - Command::Server(_) => { - builder = builder - .set_default("server.host", HOST)? - .set_default("server.port", PORT)?; - } - Command::Decode(_) => { - builder = builder - .set_default("decoder.system", SYSTEM_NAME)? - .set_default("decoder.input", "")?; - } - Command::Encode(_) => { - builder = builder - .set_default("encoder.system", SYSTEM_NAME)? - .set_default("encoder.input", "")?; - } - Command::ImportDict(_) => { - builder = builder - .set_default("import_dict.name", IMPORT_DICT_NAME)? - .set_default("import_dict.path", IMPORT_DICT_PATH)?; - } - } - - Ok(builder) -}