diff --git a/app/src/commands.rs b/app/src/commands.rs index d8ad6f7..54d4a8b 100644 --- a/app/src/commands.rs +++ b/app/src/commands.rs @@ -1,11 +1,9 @@ pub mod decode; pub mod encode; pub mod import_dict; -pub mod server; use crate::config::AppConfig; use crate::container::Container; -use crate::defaults; use anyhow::Result; use async_trait::async_trait; use clap::Subcommand; @@ -16,9 +14,6 @@ use std::path::PathBuf; #[derive(Subcommand, Debug, Clone)] pub enum Command { - /// Start the application server - Server(server::ServerArgs), - /// Decode a word using given system Decode(decode::DecodeArgs), @@ -31,7 +26,6 @@ pub enum Command { pub fn resolve_command(command: &Command) -> &dyn AppCommand { match command { - Command::Server(args) => args, Command::Decode(args) => args, Command::Encode(args) => args, Command::ImportDict(args) => args, @@ -79,3 +73,10 @@ pub trait CommandExecutor { pub trait AppCommand: ConfigurableCommand + CommandExecutor {} 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); +} diff --git a/app/src/commands/decode.rs b/app/src/commands/decode.rs index cbfd170..acdfa32 100644 --- a/app/src/commands/decode.rs +++ b/app/src/commands/decode.rs @@ -3,22 +3,12 @@ use crate::config::AppConfig; use crate::config::System; use crate::container::Container; -use anyhow::Result; -use applib::SystemDecoder; -use applib::sys_major::decoder::Decoder; -use applib::sys_major::{self as major}; +use anyhow::{Context, Result}; use async_trait::async_trait; use config::ConfigBuilder; use config::builder::DefaultState; use serde::Deserialize; -mod defaults { - use const_format::formatcp; - pub const DEC_SYSTEM_NAME: &str = "major_pl"; - pub const HELP_DEC_SYSTEM: &str = formatcp!("System to use [default: {}]", DEC_SYSTEM_NAME); - pub const HELP_DEC_INPUT: &str = formatcp!("Text to decode"); -} - #[derive(Debug, Deserialize, Clone)] pub struct Config { pub system: System, @@ -27,10 +17,10 @@ pub struct Config { #[derive(ClapArgs, Debug, Clone)] pub struct DecodeArgs { - #[arg(long, help = defaults::HELP_DEC_SYSTEM)] + #[arg(short, long, help = defaults::HELP_DEC_SYSTEM)] pub system: Option, - #[arg(long, help = defaults::HELP_DEC_INPUT)] + #[arg(short, long, help = defaults::HELP_DEC_INPUT)] pub input: String, } @@ -40,8 +30,8 @@ impl ConfigurableCommand for DecodeArgs { builder: ConfigBuilder, ) -> Result> { builder - .set_default("decoder.system", defaults::DEC_SYSTEM_NAME)? - .set_default("decoder.input", "") + .set_default("decode.system", defaults::DEC_SYSTEM_NAME)? + .set_default("decode.input", "") .map_err(Into::into) } @@ -51,27 +41,37 @@ impl ConfigurableCommand for DecodeArgs { ) -> Result> { let mut builder = builder; if let Some(ref system) = self.system { - builder = builder.set_override("decoder.system", system.clone())?; + builder = builder.set_override("decode.system", system.clone())?; } - builder = builder.set_override("decoder.input", self.input.clone())?; + builder = builder.set_override("decode.input", self.input.clone())?; + Ok(builder) } } #[async_trait] impl CommandExecutor for DecodeArgs { - async fn execute(&self, config: &AppConfig, _container: &Container) -> Result<()> { - let config = config.decoder.as_ref().expect("Decoder config not set"); + async fn execute(&self, config: &AppConfig, container: &Container) -> Result<()> { + let config = config + .decode + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Decoder config missing"))?; - let decoder: Box = match config.system { - System::MajorPl => Box::new(Decoder::new(major::rules_pl::get_rules())), - System::MajorEn => Box::new(Decoder::new(major::rules_en::get_rules())), - }; + let decoder = container.create_decoder(&config)?; - match decoder.decode(&config.input) { - Ok(result) => println!("{:?}", result), - Err(e) => eprintln!("Error: {}", e), - } - Ok(()) // TODO: Map decode result + let result = decoder + .decode(&config.input) + .with_context(|| format!("Failed to decode input: {}", config.input))?; + + let json = serde_json::to_string_pretty(&result).expect("JSON serialization failed"); + println!("{}", json); + Ok(()) } } + +mod defaults { + use const_format::formatcp; + pub const DEC_SYSTEM_NAME: &str = "major_pl"; + pub const HELP_DEC_SYSTEM: &str = formatcp!("System to use [default: {}]", DEC_SYSTEM_NAME); + pub const HELP_DEC_INPUT: &str = formatcp!("Text to decode"); +} diff --git a/app/src/commands/encode.rs b/app/src/commands/encode.rs index 5dd8aed..6e7e22f 100644 --- a/app/src/commands/encode.rs +++ b/app/src/commands/encode.rs @@ -1,9 +1,4 @@ -use applib::SystemEncoder; -use applib::sys_major::encoder::Encoder; -use applib::sys_major::{self as major, LenValueMap}; - use serde::Deserialize; -use tracing::debug; use crate::commands::{ClapArgs, CommandExecutor, ConfigurableCommand}; use crate::config::{AppConfig, System}; @@ -14,25 +9,22 @@ use async_trait::async_trait; use config::ConfigBuilder; use config::builder::DefaultState; -mod defaults { - use const_format::formatcp; - pub const ENC_SYSTEM_NAME: &str = "major_pl"; - pub const HELP_ENC_SYSTEM: &str = formatcp!("System to use [default: {}]", ENC_SYSTEM_NAME); - pub const HELP_ENC_INPUT: &str = formatcp!("Number to encode"); -} - #[derive(Debug, Deserialize, Clone)] pub struct Config { pub system: System, pub input: String, + pub dict_name: String, } #[derive(ClapArgs, Debug, Clone)] pub struct EncodeArgs { - #[arg(long, help = defaults::HELP_ENC_SYSTEM)] + #[arg(short, long, help = defaults::HELP_ENC_SYSTEM)] pub system: Option, - #[arg(long, help = defaults::HELP_ENC_INPUT)] + #[arg(short, long, help = defaults::HELP_ENC_DICT)] + pub dict_name: Option, + + #[arg(short, long, help = defaults::HELP_ENC_INPUT)] pub input: String, } @@ -42,8 +34,9 @@ impl ConfigurableCommand for EncodeArgs { builder: ConfigBuilder, ) -> Result> { builder - .set_default("encoder.system", defaults::ENC_SYSTEM_NAME)? - .set_default("encoder.input", "") + .set_default("encode.system", defaults::ENC_SYSTEM_NAME)? + .set_default("encode.dict_name", defaults::ENC_DICT_NAME)? + .set_default("encode.input", "") .map_err(Into::into) } @@ -52,10 +45,14 @@ impl ConfigurableCommand for EncodeArgs { builder: ConfigBuilder, ) -> Result> { let mut builder = builder; - if let Some(ref system) = self.system { - builder = builder.set_override("encoder.system", system.clone())?; + + if let Some(system) = &self.system { + builder = builder.set_override("encode.system", system.clone())?; + } + if let Some(dict_name) = &self.dict_name { + builder = builder.set_override("encode.dict_name", dict_name.clone())?; } - builder = builder.set_override("encoder.input", self.input.clone())?; + builder = builder.set_override("encode.input", self.input.clone())?; Ok(builder) } } @@ -63,25 +60,13 @@ impl ConfigurableCommand for EncodeArgs { #[async_trait] impl CommandExecutor for EncodeArgs { async fn execute(&self, config: &AppConfig, container: &Container) -> Result<()> { - let config = config.encoder.as_ref().expect("Encoder config not set"); - let repo = container.create_dict_repo("demo_pl").await?; + let config = config + .encode + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Encoder config not set"))?; - debug!("Running encoder with config {:?}", config); - - let system_encoder: Box = match config.system { - System::MajorPl | System::MajorEn => { - let decoder = major::Decoder::new(match config.system { - System::MajorPl => major::rules_pl::get_rules(), - System::MajorEn => major::rules_en::get_rules(), - // _ => unreachable!(), - }); - let stream = repo.stream_batches(100).await.unwrap(); - let lvmap = LenValueMap::from_stream(stream, &decoder).await.unwrap(); - Box::new(Encoder::new(lvmap)) - } - }; - - let result = system_encoder.encode(&config.input); + let encoder = container.create_encoder(&config).await?; + let result = encoder.encode(&config.input); match result { Ok(res) => { @@ -94,3 +79,12 @@ impl CommandExecutor for EncodeArgs { Ok(()) } } + +mod defaults { + use const_format::formatcp; + pub const ENC_SYSTEM_NAME: &str = "major_pl"; + pub const ENC_DICT_NAME: &str = "demo_pl"; + pub const HELP_ENC_SYSTEM: &str = formatcp!("System to use [default: {}]", ENC_SYSTEM_NAME); + pub const HELP_ENC_INPUT: &str = formatcp!("Number to encode"); + pub const HELP_ENC_DICT: &str = formatcp!("Dictionary to use [default: {}]", ENC_DICT_NAME); +} diff --git a/app/src/commands/import_dict.rs b/app/src/commands/import_dict.rs index 2532acf..6d9041d 100644 --- a/app/src/commands/import_dict.rs +++ b/app/src/commands/import_dict.rs @@ -1,7 +1,6 @@ use crate::commands::{ClapArgs, CommandExecutor, ConfigurableCommand}; use crate::config::AppConfig; use crate::container::Container; -use crate::defaults; use anyhow::Result; use async_trait::async_trait; use config::ConfigBuilder; @@ -62,3 +61,11 @@ impl CommandExecutor for ImportDictArgs { Ok(()) } } + +mod defaults { + use const_format::formatcp; + pub const IMPORT_DICT_NAME: &str = ""; + pub const IMPORT_DICT_PATH: &str = ""; + pub const HELP_IMPORT_DICT_NAME: &str = formatcp!("Dictionary name"); + pub const HELP_IMPORT_DICT_INPUT: &str = formatcp!("Dictionary file path"); +} diff --git a/app/src/commands/server.rs b/app/src/commands/server.rs deleted file mode 100644 index a538b5e..0000000 --- a/app/src/commands/server.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::commands::{ClapArgs, CommandExecutor, ConfigurableCommand}; -use crate::config::AppConfig; -use crate::container::Container; - -use crate::defaults; -use anyhow::Result; -use async_trait::async_trait; -use config::ConfigBuilder; -use config::builder::DefaultState; -use serde::Deserialize; -use tokio::signal; -use tracing::{info, warn}; - -#[derive(Debug, Deserialize, Clone)] -pub struct Config { - pub port: u16, -} - -#[derive(ClapArgs, Debug, Clone)] -pub struct ServerArgs { - #[arg(short, long, help = defaults::HELP_PORT)] - 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) - } -} - -#[async_trait] -impl CommandExecutor for ServerArgs { - async fn execute(&self, config: &AppConfig, _container: &Container) -> Result<()> { - let config = config.server.as_ref().expect("Server config not set"); - - info!("Running server with config: {:#?}", config); - tokio::select! { - _ = server_loop() => {}, - _ = wait_for_shutdown_signal() => { - info!("Shutting down server..."); - } - } - - 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); - } - } -} - -async fn server_loop() { - loop { - tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; - info!("Health check... "); - } -} diff --git a/app/src/config.rs b/app/src/config.rs index 8feff24..8635dd1 100644 --- a/app/src/config.rs +++ b/app/src/config.rs @@ -7,11 +7,9 @@ use serde::Deserialize; #[derive(Debug, Deserialize, Clone)] pub struct AppConfig { #[serde(default)] - pub server: Option, + pub decode: Option, #[serde(default)] - pub decoder: Option, - #[serde(default)] - pub encoder: Option, + pub encode: Option, #[serde(default)] pub import_dict: Option, pub log_level: String, @@ -50,8 +48,6 @@ impl AppConfig { } // TODO: move? -use applib::sys_major::{self as major, LenValueMap}; -use applib::{DictRepository, SystemDecoder, SystemEncoder}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] pub enum System { @@ -71,24 +67,3 @@ impl From<&str> for System { } } } - -pub fn create_decoder(system: &System) -> Box { - match system { - System::MajorPl => Box::new(major::Decoder::new(major::rules_pl::get_rules())), - System::MajorEn => Box::new(major::Decoder::new(major::rules_en::get_rules())), - } -} - -pub async fn create_encoder(system: &System, dict: &dyn DictRepository) -> Box { - // let decoder = create_decoder(&system); - let decoder = major::Decoder::new(match system { - System::MajorPl => major::rules_pl::get_rules(), - System::MajorEn => major::rules_en::get_rules(), - }); - let stream = dict.stream_batches(100).await.unwrap(); // TODO - let lvmap = LenValueMap::from_stream(stream, &decoder).await.unwrap(); // TODO - match system { - System::MajorPl => Box::new(major::Encoder::new(lvmap)), - System::MajorEn => Box::new(major::Encoder::new(lvmap)), - } -} diff --git a/app/src/container.rs b/app/src/container.rs index f67bb96..0a82a73 100644 --- a/app/src/container.rs +++ b/app/src/container.rs @@ -4,6 +4,13 @@ use std::sync::Arc; use applib::DictImporter; use applib::DictRepository; use applib::SqliteDictRepository; +use applib::SystemDecoder; +use applib::SystemEncoder; +use applib::sys_major as major; + +use crate::commands::decode; +use crate::commands::encode; +use crate::config::System; #[derive(Clone)] pub struct Container; @@ -26,4 +33,36 @@ impl Container { dict_repo.use_dict(dict_name); Ok(Arc::new(dict_repo)) } + + pub fn create_decoder( + &self, + config: &decode::Config, + ) -> anyhow::Result> { + Ok(match config.system { + System::MajorPl => Box::new(major::Decoder::new(major::rules_pl::get_rules())), + System::MajorEn => Box::new(major::Decoder::new(major::rules_en::get_rules())), + }) + } + + pub async fn create_encoder( + &self, + config: &encode::Config, + ) -> anyhow::Result> { + let dict = self.create_dict_repo(&config.dict_name).await?; + + let dec_config = decode::Config { + system: config.system.clone(), + input: String::new(), + }; + let decoder = self.create_decoder(&dec_config)?; + + let words_stream = dict.stream_batches(1000).await.unwrap(); + + let lvmap = major::LenValueMap::from_stream(words_stream, &(*decoder)) + .await + .unwrap(); + + let encoder = major::Encoder::new(lvmap); + Ok(Box::new(encoder)) + } } diff --git a/app/src/defaults.rs b/app/src/defaults.rs deleted file mode 100644 index a54bf2d..0000000 --- a/app/src/defaults.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub const HOST: &str = "127.0.0.1"; -pub const PORT: u16 = 8080; -pub const LOG_LEVEL: &str = "info"; -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_IMPORT_DICT_NAME: &str = formatcp!("Dictionary name"); -pub const HELP_IMPORT_DICT_INPUT: &str = formatcp!("Dictionary file path"); diff --git a/app/src/main.rs b/app/src/main.rs index f51bc56..377c91b 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -2,7 +2,6 @@ mod app; mod commands; mod config; mod container; -mod defaults; use anyhow::Result; use app::Application; diff --git a/lib/src/common/entities.rs b/lib/src/common/entities.rs index 720d554..ffb209b 100644 --- a/lib/src/common/entities.rs +++ b/lib/src/common/entities.rs @@ -37,7 +37,7 @@ impl Deref for EncodedValue { /// and dictionary words (reasonable length), we can use /// u64 (20-digit number), but the whole input text can /// be longer than 20 digits, so we operate on String (<= 255). -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Serialize, Clone, PartialEq, Eq)] pub struct DecodedValue(String); impl DecodedValue {