23 changed files with 8978 additions and 409 deletions
@ -1,5 +0,0 @@ |
|||||||
pub mod args; |
|
||||||
pub mod commands; |
|
||||||
pub mod defaults; |
|
||||||
pub mod handlers; |
|
||||||
pub mod traits; |
|
||||||
@ -1,168 +0,0 @@ |
|||||||
use crate::cli::defaults; |
|
||||||
use crate::cli::traits::ConfigurableCommand; |
|
||||||
use anyhow::Result; |
|
||||||
use clap::{Args as ClapArgs, Parser, Subcommand}; |
|
||||||
use config::ConfigBuilder; |
|
||||||
use config::builder::DefaultState; |
|
||||||
use std::path::PathBuf; |
|
||||||
|
|
||||||
#[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<String>, |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Subcommand, Debug, Clone)] |
|
||||||
pub enum Command { |
|
||||||
/// Start the application server
|
|
||||||
Server(ServerArgs), |
|
||||||
|
|
||||||
/// Decode a word using given system
|
|
||||||
Decode(DecodeArgs), |
|
||||||
|
|
||||||
/// Encode a number using given system
|
|
||||||
Encode(EncodeArgs), |
|
||||||
|
|
||||||
/// Import dictionary
|
|
||||||
ImportDict(ImportDictArgs), |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(ClapArgs, Debug, Clone)] |
|
||||||
pub struct ServerArgs { |
|
||||||
#[arg(short, long, help = defaults::HELP_PORT)] |
|
||||||
pub port: Option<u16>, |
|
||||||
} |
|
||||||
|
|
||||||
impl ConfigurableCommand for ServerArgs { |
|
||||||
fn apply_defaults( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
builder |
|
||||||
.set_default("server.host", defaults::HOST)? |
|
||||||
.set_default("server.port", defaults::PORT) |
|
||||||
.map_err(Into::into) |
|
||||||
} |
|
||||||
|
|
||||||
fn apply_overrides( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
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)] |
|
||||||
pub system: Option<String>, |
|
||||||
|
|
||||||
#[arg(long, help = defaults::HELP_DEC_INPUT)] |
|
||||||
pub input: String, |
|
||||||
} |
|
||||||
|
|
||||||
impl ConfigurableCommand for DecodeArgs { |
|
||||||
fn apply_defaults( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
builder |
|
||||||
.set_default("decoder.system", defaults::SYSTEM_NAME)? |
|
||||||
.set_default("decoder.input", "") |
|
||||||
.map_err(Into::into) |
|
||||||
} |
|
||||||
|
|
||||||
fn apply_overrides( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
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)] |
|
||||||
pub system: Option<String>, |
|
||||||
|
|
||||||
#[arg(long, help = defaults::HELP_ENC_INPUT)] |
|
||||||
pub input: String, |
|
||||||
} |
|
||||||
|
|
||||||
impl ConfigurableCommand for EncodeArgs { |
|
||||||
fn apply_defaults( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
builder |
|
||||||
.set_default("encoder.system", defaults::SYSTEM_NAME)? |
|
||||||
.set_default("encoder.input", "") |
|
||||||
.map_err(Into::into) |
|
||||||
} |
|
||||||
|
|
||||||
fn apply_overrides( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
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)] |
|
||||||
pub name: String, |
|
||||||
|
|
||||||
#[arg(long, help = defaults::HELP_IMPORT_DICT_INPUT)] |
|
||||||
pub path: String, |
|
||||||
} |
|
||||||
|
|
||||||
impl ConfigurableCommand for ImportDictArgs { |
|
||||||
fn apply_defaults( |
|
||||||
&self, |
|
||||||
builder: ConfigBuilder<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
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<DefaultState>, |
|
||||||
) -> Result<ConfigBuilder<DefaultState>> { |
|
||||||
builder |
|
||||||
.set_override("import_dict.name", self.name.clone())? |
|
||||||
.set_override("import_dict.path", self.path.clone()) |
|
||||||
.map_err(Into::into) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
pub mod decode; |
|
||||||
pub mod encode; |
|
||||||
pub mod import_dict; |
|
||||||
pub mod server; |
|
||||||
@ -1,22 +0,0 @@ |
|||||||
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), |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,36 +0,0 @@ |
|||||||
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), |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,12 +0,0 @@ |
|||||||
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(()) |
|
||||||
} |
|
||||||
@ -1,6 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
@ -1,81 +0,0 @@ |
|||||||
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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,14 +0,0 @@ |
|||||||
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>>; |
|
||||||
} |
|
||||||
@ -0,0 +1,81 @@ |
|||||||
|
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; |
||||||
|
use clap::{Args as ClapArgs, Parser}; |
||||||
|
use config::ConfigBuilder; |
||||||
|
use config::builder::DefaultState; |
||||||
|
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), |
||||||
|
|
||||||
|
/// Encode a number using given system
|
||||||
|
Encode(encode::EncodeArgs), |
||||||
|
|
||||||
|
/// Import dictionary
|
||||||
|
ImportDict(import_dict::ImportDictArgs), |
||||||
|
} |
||||||
|
|
||||||
|
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, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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<String>, |
||||||
|
} |
||||||
|
|
||||||
|
pub trait ConfigurableCommand { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>>; |
||||||
|
fn apply_overrides( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>>; |
||||||
|
} |
||||||
|
|
||||||
|
#[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 {} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
use crate::commands::{ClapArgs, CommandExecutor, ConfigurableCommand}; |
||||||
|
use crate::config::AppConfig; |
||||||
|
use crate::container::Container; |
||||||
|
|
||||||
|
use crate::defaults; |
||||||
|
use anyhow::Result; |
||||||
|
use applib::core::sys_major::decoder::Decoder; |
||||||
|
use applib::core::sys_major::{self as major}; |
||||||
|
use applib::core::traits::SystemDecoder; |
||||||
|
use applib::system::System; |
||||||
|
use async_trait::async_trait; |
||||||
|
use config::ConfigBuilder; |
||||||
|
use config::builder::DefaultState; |
||||||
|
|
||||||
|
#[derive(ClapArgs, Debug, Clone)] |
||||||
|
pub struct DecodeArgs { |
||||||
|
#[arg(long, help = defaults::HELP_DEC_SYSTEM)] |
||||||
|
pub system: Option<String>, |
||||||
|
|
||||||
|
#[arg(long, help = defaults::HELP_DEC_INPUT)] |
||||||
|
pub input: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConfigurableCommand for DecodeArgs { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
builder |
||||||
|
.set_default("decoder.system", defaults::SYSTEM_NAME)? |
||||||
|
.set_default("decoder.input", "") |
||||||
|
.map_err(Into::into) |
||||||
|
} |
||||||
|
|
||||||
|
fn apply_overrides( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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"); |
||||||
|
|
||||||
|
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())), |
||||||
|
}; |
||||||
|
|
||||||
|
match decoder.decode(&config.input) { |
||||||
|
Ok(result) => println!("{:?}", result), |
||||||
|
Err(e) => eprintln!("Error: {}", e), |
||||||
|
} |
||||||
|
Ok(()) // TODO: Map decode result
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,83 @@ |
|||||||
|
use applib::core::sys_major::encoder::Encoder; |
||||||
|
use applib::core::sys_major::{self as major, LenValueMap}; |
||||||
|
use applib::core::traits::SystemEncoder; |
||||||
|
use applib::system::System; |
||||||
|
use tracing::debug; |
||||||
|
|
||||||
|
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; |
||||||
|
|
||||||
|
#[derive(ClapArgs, Debug, Clone)] |
||||||
|
pub struct EncodeArgs { |
||||||
|
#[arg(long, help = defaults::HELP_ENC_SYSTEM)] |
||||||
|
pub system: Option<String>, |
||||||
|
|
||||||
|
#[arg(long, help = defaults::HELP_ENC_INPUT)] |
||||||
|
pub input: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConfigurableCommand for EncodeArgs { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
builder |
||||||
|
.set_default("encoder.system", defaults::SYSTEM_NAME)? |
||||||
|
.set_default("encoder.input", "") |
||||||
|
.map_err(Into::into) |
||||||
|
} |
||||||
|
|
||||||
|
fn apply_overrides( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
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) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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?; |
||||||
|
|
||||||
|
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 = 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); |
||||||
|
|
||||||
|
match result { |
||||||
|
Ok(res) => { |
||||||
|
let json = serde_json::to_string_pretty(&res).expect("JSON serialization failed"); |
||||||
|
println!("{}", json); |
||||||
|
} |
||||||
|
Err(e) => eprintln!("Error encoding: {:?}", e), |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
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; |
||||||
|
|
||||||
|
#[derive(ClapArgs, Debug, Clone)] |
||||||
|
pub struct ImportDictArgs { |
||||||
|
#[arg(long, help = defaults::HELP_IMPORT_DICT_NAME)] |
||||||
|
pub name: String, |
||||||
|
|
||||||
|
#[arg(long, help = defaults::HELP_IMPORT_DICT_INPUT)] |
||||||
|
pub path: String, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConfigurableCommand for ImportDictArgs { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
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<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
builder |
||||||
|
.set_override("import_dict.name", self.name.clone())? |
||||||
|
.set_override("import_dict.path", self.path.clone()) |
||||||
|
.map_err(Into::into) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[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?; |
||||||
|
|
||||||
|
// 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,60 @@ |
|||||||
|
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 tokio::signal; |
||||||
|
use tracing::{info, warn}; |
||||||
|
|
||||||
|
#[derive(ClapArgs, Debug, Clone)] |
||||||
|
pub struct ServerArgs { |
||||||
|
#[arg(short, long, help = defaults::HELP_PORT)] |
||||||
|
pub port: Option<u16>, |
||||||
|
} |
||||||
|
|
||||||
|
impl ConfigurableCommand for ServerArgs { |
||||||
|
fn apply_defaults( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
builder |
||||||
|
.set_default("server.host", defaults::HOST)? |
||||||
|
.set_default("server.port", defaults::PORT) |
||||||
|
.map_err(Into::into) |
||||||
|
} |
||||||
|
|
||||||
|
fn apply_overrides( |
||||||
|
&self, |
||||||
|
builder: ConfigBuilder<DefaultState>, |
||||||
|
) -> Result<ConfigBuilder<DefaultState>> { |
||||||
|
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"); |
||||||
|
applib::presentation::server::run(config.clone(), wait_for_shutdown_signal()).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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@ |
|||||||
|
[ |
||||||
|
{"word": "hello", "metadata": {"type": "greeting", "language": "english"}}, |
||||||
|
{"word": "world", "metadata": {"type": "noun", "language": "english"}}, |
||||||
|
{"word": "rust", "metadata": {"type": "programming_language", "paradigm": "systems"}}, |
||||||
|
{"word": "programming", "metadata": {"type": "verb", "context": "computing"}}, |
||||||
|
{"word": "database", "metadata": {"type": "noun", "context": "data_storage"}}, |
||||||
|
{"word": "sqlite", "metadata": {"type": "database_engine", "features": ["embedded", "sql"]}}, |
||||||
|
{"word": "json", "metadata": {"type": "data_format", "standard": "RFC 8259"}}, |
||||||
|
{"word": "import", "metadata": {"type": "verb", "context": "data_operations"}}, |
||||||
|
{"word": "dictionary", "metadata": {"type": "noun", "context": "reference"}}, |
||||||
|
{"word": "example", "metadata": {"type": "noun", "usage": "demonstration"}} |
||||||
|
] |
||||||
Loading…
Reference in new issue