21 changed files with 191 additions and 64 deletions
@ -0,0 +1,5 @@ |
|||||||
|
pub mod args; |
||||||
|
pub mod commands; |
||||||
|
pub mod defaults; |
||||||
|
pub mod handlers; |
||||||
|
pub mod traits; |
||||||
@ -1,4 +1,5 @@ |
|||||||
use crate::cli::{ConfigurableCommand, defaults}; |
use crate::cli::defaults; |
||||||
|
use crate::cli::traits::ConfigurableCommand; |
||||||
use anyhow::Result; |
use anyhow::Result; |
||||||
use clap::{Args as ClapArgs, Parser, Subcommand}; |
use clap::{Args as ClapArgs, Parser, Subcommand}; |
||||||
use config::ConfigBuilder; |
use config::ConfigBuilder; |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
use applib::application::config::DecoderConfig; |
||||||
|
use applib::core::sys_major::decoder::Decoder; |
||||||
|
use applib::core::sys_major::{self as major, LenValueMap}; |
||||||
|
use applib::core::traits::SystemDecoder; |
||||||
|
use applib::system::System; |
||||||
|
use std::sync::Arc; |
||||||
|
|
||||||
|
pub async fn run(config: DecoderConfig) { |
||||||
|
let decoder: Box<dyn SystemDecoder> = 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())), |
||||||
|
// _ => {
|
||||||
|
// eprintln!("Unknown system: {:?}", config.system);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}; |
||||||
|
|
||||||
|
match decoder.decode(&config.input) { |
||||||
|
Ok(result) => println!("{:?}", result), |
||||||
|
Err(e) => eprintln!("Error: {}", e), |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
use applib::application::config::EncoderConfig; |
||||||
|
use applib::core::sys_major::encoder::Encoder; |
||||||
|
use applib::core::sys_major::{self as major, LenValueMap}; |
||||||
|
use applib::core::traits::{DictRepository, SystemEncoder}; |
||||||
|
use applib::system::System; |
||||||
|
use tracing::debug; |
||||||
|
|
||||||
|
pub async fn run(config: EncoderConfig, dict: &dyn DictRepository) { |
||||||
|
debug!("Running encoder with config {:?}", config); |
||||||
|
|
||||||
|
let system_encoder: Box<dyn SystemEncoder> = 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 = dict.stream_batches(100).await.unwrap(); |
||||||
|
let lvmap = LenValueMap::from_stream(stream, &decoder).await.unwrap(); |
||||||
|
Box::new(Encoder::new(lvmap)) |
||||||
|
} // _ => {
|
||||||
|
// eprintln!("Unknown system: {:?}", config.system);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
}; |
||||||
|
|
||||||
|
let result = system_encoder.encode(&config.input); |
||||||
|
|
||||||
|
match result { |
||||||
|
Ok(res) => { |
||||||
|
let json = serde_json::to_string_pretty(&res).expect("JSON serialization failed"); |
||||||
|
println!("{}", json); |
||||||
|
} |
||||||
|
Err(e) => eprintln!("Error encoding: {:?}", e), |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
use anyhow::Result; |
||||||
|
use applib::DictImporter; |
||||||
|
use applib::application::config::ImportDictConfig; |
||||||
|
|
||||||
|
pub async fn run(config: ImportDictConfig, importer: DictImporter) -> Result<()> { |
||||||
|
// Importer expects an impl DictSource
|
||||||
|
// We need to create a DictSource from the path
|
||||||
|
use applib::infrastructure::json_file_dict_source::JsonFileDictSource; |
||||||
|
let source = JsonFileDictSource::new(&config.path)?; |
||||||
|
importer.import(source).await?; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
use applib::application::config::ServerConfig; |
||||||
|
use std::future::Future; |
||||||
|
|
||||||
|
pub async fn run(config: ServerConfig, signal: impl Future<Output = ()> + Send + 'static) { |
||||||
|
applib::presentation::server::run(config, signal).await; |
||||||
|
} |
||||||
@ -0,0 +1,81 @@ |
|||||||
|
use crate::cli::args::{Command, DecodeArgs, EncodeArgs, ImportDictArgs, ServerArgs}; |
||||||
|
use crate::cli::commands; |
||||||
|
use crate::cli::traits::ConfigurableCommand; |
||||||
|
use crate::config::AppConfig; |
||||||
|
use crate::container::Container; |
||||||
|
use anyhow::Result; |
||||||
|
use async_trait::async_trait; |
||||||
|
use tokio::signal; |
||||||
|
use tracing::{info, warn}; |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
pub trait CommandExecutor { |
||||||
|
async fn execute(&self, config: &AppConfig, container: &Container) -> Result<()>; |
||||||
|
} |
||||||
|
|
||||||
|
// AppCommand must be dyn-compatible. ConfigurableCommand is already dyn-compatible.
|
||||||
|
// CommandExecutor is dyn-compatible because of #[async_trait].
|
||||||
|
pub trait AppCommand: ConfigurableCommand + CommandExecutor {} |
||||||
|
|
||||||
|
impl<T: ConfigurableCommand + CommandExecutor> AppCommand for T {} |
||||||
|
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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"); |
||||||
|
commands::server::run(config.clone(), wait_for_shutdown_signal()).await; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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"); |
||||||
|
commands::decode::run(config.clone()).await; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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?; |
||||||
|
commands::encode::run(config.clone(), repo.as_ref()).await; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl CommandExecutor for ImportDictArgs { |
||||||
|
async fn execute(&self, config: &AppConfig, container: &Container) -> Result<()> { |
||||||
|
let config = config |
||||||
|
.import_dict |
||||||
|
.as_ref() |
||||||
|
.expect("ImportDict config not set"); |
||||||
|
let importer = container.create_dict_importer(&config.name).await?; |
||||||
|
commands::import_dict::run(config.clone(), 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
use anyhow::Result; |
||||||
|
pub use config::ConfigBuilder; |
||||||
|
pub use config::builder::DefaultState; |
||||||
|
|
||||||
|
pub trait ConfigurableCommand { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>>; |
||||||
|
fn apply_overrides( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>>; |
||||||
|
} |
||||||
@ -1,10 +1,10 @@ |
|||||||
pub mod application; |
pub mod application; |
||||||
mod core; |
pub mod core; |
||||||
pub mod infrastructure; |
pub mod infrastructure; |
||||||
mod presentation; |
pub mod presentation; |
||||||
|
|
||||||
pub use self::application::config; |
pub use self::application::config; |
||||||
pub use self::application::services::DictImporter; |
pub use self::application::services::DictImporter; |
||||||
pub use self::core::system; |
pub use self::core::system; |
||||||
pub use self::core::traits; |
pub use self::core::traits; |
||||||
pub use self::presentation::cli; |
// pub use self::presentation::cli; // Removed as we are deleting it
|
||||||
|
|||||||
@ -1,10 +0,0 @@ |
|||||||
use crate::application::config::DecoderConfig; |
|
||||||
use crate::core::system; |
|
||||||
use tracing::debug; |
|
||||||
|
|
||||||
pub async fn run(config: DecoderConfig) { |
|
||||||
debug!("Running decoder with config {:?}", config); |
|
||||||
let decoder = system::create_decoder(&config.system); |
|
||||||
let result = decoder.decode(&config.input).unwrap(); |
|
||||||
println!("{}", result.as_str()); |
|
||||||
} |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
use crate::application::config::EncoderConfig; |
|
||||||
use crate::core::{DictRepository, system}; |
|
||||||
use tracing::debug; |
|
||||||
|
|
||||||
pub async fn run(config: EncoderConfig, dict: &dyn DictRepository) { |
|
||||||
debug!("Running encoder with config {:?}", config); |
|
||||||
let encoder = system::create_encoder(&config.system, dict).await; |
|
||||||
let result = encoder.encode(&config.input).unwrap(); |
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(&result).expect("JSON serialization failed"); |
|
||||||
|
|
||||||
println!("{}", json); |
|
||||||
} |
|
||||||
@ -1,27 +0,0 @@ |
|||||||
use crate::application::{config::ImportDictConfig, services::DictImporter}; |
|
||||||
use crate::infrastructure::json_file_dict_source::JsonFileDictSource; |
|
||||||
use tracing::{debug, error, info}; |
|
||||||
|
|
||||||
pub async fn run(config: ImportDictConfig, importer: DictImporter) -> Result<(), anyhow::Error> { |
|
||||||
debug!("Importing dict with config {:?}", config); |
|
||||||
|
|
||||||
info!( |
|
||||||
"Starting import of dictionary '{}' from file '{}'", |
|
||||||
config.name, config.path |
|
||||||
); |
|
||||||
|
|
||||||
// Create the JSON file source (will auto-generate IDs starting from 1)
|
|
||||||
let source = JsonFileDictSource::new(&config.path)?; |
|
||||||
|
|
||||||
// Perform the import (this will call create() first)
|
|
||||||
match importer.import(source).await { |
|
||||||
Ok(()) => { |
|
||||||
info!("Successfully imported dictionary '{}'", config.name); |
|
||||||
Ok(()) |
|
||||||
} |
|
||||||
Err(e) => { |
|
||||||
error!("Failed to import dictionary '{}': {}", config.name, e); |
|
||||||
Err(e) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue