diff options
| author | rtkay123 <dev@kanjala.com> | 2026-04-03 12:48:35 +0200 |
|---|---|---|
| committer | rtkay123 <dev@kanjala.com> | 2026-04-03 12:48:35 +0200 |
| commit | 898a2966975c7397e35d8df6b72df42147bf18bd (patch) | |
| tree | 26d1f15cf4ea3baa7b6f6113828b37eaf839fe66 /crates | |
| parent | 2aec89e22ac8ebcc2859a7492cfca9b5d81cdcc4 (diff) | |
| download | sellershut-898a2966975c7397e35d8df6b72df42147bf18bd.tar.bz2 sellershut-898a2966975c7397e35d8df6b72df42147bf18bd.zip | |
feat: impl config
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/sellershut/Cargo.toml | 8 | ||||
| -rw-r--r-- | crates/sellershut/src/config/cli.rs | 25 | ||||
| -rw-r--r-- | crates/sellershut/src/config/mod.rs | 67 | ||||
| -rw-r--r-- | crates/sellershut/src/config/server.rs | 63 | ||||
| -rw-r--r-- | crates/sellershut/src/main.rs | 25 |
5 files changed, 186 insertions, 2 deletions
diff --git a/crates/sellershut/Cargo.toml b/crates/sellershut/Cargo.toml index 1ce8f74..9ded17b 100644 --- a/crates/sellershut/Cargo.toml +++ b/crates/sellershut/Cargo.toml @@ -8,3 +8,11 @@ documentation.workspace = true homepage.workspace = true [dependencies] +anyhow = "1.0.102" +axum = { version = "0.8.8", features = ["macros"] } +clap = { version = "4.6.0", features = ["derive", "env"] } +serde = { workspace = true, features = ["derive"] } +tokio = { version = "1.51.0", features = ["macros", "rt", "rt-multi-thread"] } +toml = "1.1.2" +tracing.workspace = true +tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } diff --git a/crates/sellershut/src/config/cli.rs b/crates/sellershut/src/config/cli.rs new file mode 100644 index 0000000..bcbf404 --- /dev/null +++ b/crates/sellershut/src/config/cli.rs @@ -0,0 +1,25 @@ +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +#[derive(Debug, Parser)] +/// A federated marketplace platform +pub struct Cli { + #[command(subcommand)] + pub command: Option<Commands>, + #[command(flatten)] + pub config: super::Config, +} + +#[derive(Debug, Subcommand)] +pub enum Commands { + #[command(name = "generate-config")] + GenerateConfig { + /// Directory to write the config file into + #[arg(long)] + dir: PathBuf, + /// Output filename + #[arg(long, default_value = "sellershut.toml")] + file_name: String, + }, +} diff --git a/crates/sellershut/src/config/mod.rs b/crates/sellershut/src/config/mod.rs new file mode 100644 index 0000000..b7a6ba3 --- /dev/null +++ b/crates/sellershut/src/config/mod.rs @@ -0,0 +1,67 @@ +pub mod cli; +mod server; + +use anyhow::Result; +use clap::Args; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Args, Deserialize, Serialize, Default, PartialEq, Eq)] +#[serde(default, rename_all = "kebab-case")] +/// A federated marketplace platform +pub struct Config { + /// Path to the config file to load. + #[arg(long, env = "HUT_CONFIG")] + #[serde(skip)] + config: Option<PathBuf>, + /// Server configuration. + #[command(flatten)] + server: server::ServerConfig, +} +impl Config { + pub fn load(cli: Self) -> Result<Self> { + let file = if let Some(path) = &cli.config { + read_config_file(path)? + } else { + Config::default() + }; + + Ok(file.merge(cli).with_defaults()) + } + + fn merge(self, higher: Self) -> Self { + Self { + config: higher.config.or(self.config), + server: self.server.merge(higher.server), + } + } + + fn with_defaults(self) -> Self { + Self { + config: self.config, + server: self.server.with_defaults(), + } + } + + fn defaults() -> Self { + Self { + config: None, + server: server::ServerConfig::defaults(), + } + } +} + +fn read_config_file(path: &Path) -> Result<Config> { + let raw = std::fs::read_to_string(path)?; + Ok(toml::from_str(&raw)?) +} + +pub fn generate_config_file(dir: &Path, file_name: &str) -> Result<PathBuf> { + std::fs::create_dir_all(dir)?; + let path = dir.join(file_name); + + let contents = toml::to_string_pretty(&Config::defaults())?; + std::fs::write(&path, contents)?; + + Ok(path) +} diff --git a/crates/sellershut/src/config/server.rs b/crates/sellershut/src/config/server.rs new file mode 100644 index 0000000..08b7828 --- /dev/null +++ b/crates/sellershut/src/config/server.rs @@ -0,0 +1,63 @@ +use std::path::PathBuf; + +use clap::Parser; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Parser, Deserialize, Serialize, Default, PartialEq, Eq)] +#[serde(default, rename_all = "kebab-case")] +pub struct ServerConfig { + /// Port the application server listens on. + #[arg(short, long, env = "HUT_SERVER_PORT")] + port: Option<u16>, + /// Request timeout duration + #[arg(long, env = "HUT_SERVER_TIMEOUT_SECS")] + timeout_duration: Option<u64>, + + /// Log level for the application server. + #[arg(long, env = "HUT_SERVER_LOG_LEVEL")] + log_level: Option<String>, + + /// Directory where log files should be written. + #[arg(long, env = "HUT_SERVER_LOG_FILE_DIRECTORY")] + log_directory: Option<PathBuf>, +} + +impl ServerConfig { + pub(super) fn merge(self, higher: Self) -> Self { + Self { + port: higher.port.or(self.port), + log_level: higher.log_level.or(self.log_level), + log_directory: higher.log_directory.or(self.log_directory), + timeout_duration: higher.timeout_duration.or(self.timeout_duration), + } + } + + pub(super) fn with_defaults(self) -> Self { + Self { + port: Some(self.port.unwrap_or(2210)), + log_level: Some("info".to_string()), + log_directory: Some(self.log_directory.unwrap_or_else(std::env::temp_dir)), + timeout_duration: Some(self.timeout_duration.unwrap_or(5)), + } + } + + pub(super) fn defaults() -> Self { + Self { + port: Some(2210), + log_level: Some(String::from("info")), + log_directory: Some(std::env::temp_dir()), + timeout_duration: Some(5), + } + } +} + +#[cfg(test)] +mod tests { + use crate::config::Config; + + #[test] + fn log_temp_dir() { + let cfg = Config::load(Config::default()).unwrap(); + assert_eq!(cfg.server.log_directory, Some(std::env::temp_dir())); + } +} diff --git a/crates/sellershut/src/main.rs b/crates/sellershut/src/main.rs index e7a11a9..900d554 100644 --- a/crates/sellershut/src/main.rs +++ b/crates/sellershut/src/main.rs @@ -1,3 +1,24 @@ -fn main() { - println!("Hello, world!"); +mod config; + +use anyhow::Result; +use clap::Parser; + +use crate::config::cli; + +#[tokio::main] +async fn main() -> Result<()> { + let cli = cli::Cli::parse(); + + match cli.command { + Some(cli::Commands::GenerateConfig { dir, file_name }) => { + let path = config::generate_config_file(&dir, &file_name)?; + println!("Wrote {}", path.display()); + } + None => { + let cfg = config::Config::load(cli.config)?; + println!("{cfg:#?}"); + } + } + + Ok(()) } |
