aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2026-04-03 12:48:35 +0200
committerrtkay123 <dev@kanjala.com>2026-04-03 12:48:35 +0200
commit898a2966975c7397e35d8df6b72df42147bf18bd (patch)
tree26d1f15cf4ea3baa7b6f6113828b37eaf839fe66 /crates
parent2aec89e22ac8ebcc2859a7492cfca9b5d81cdcc4 (diff)
downloadsellershut-898a2966975c7397e35d8df6b72df42147bf18bd.tar.bz2
sellershut-898a2966975c7397e35d8df6b72df42147bf18bd.zip
feat: impl config
Diffstat (limited to 'crates')
-rw-r--r--crates/sellershut/Cargo.toml8
-rw-r--r--crates/sellershut/src/config/cli.rs25
-rw-r--r--crates/sellershut/src/config/mod.rs67
-rw-r--r--crates/sellershut/src/config/server.rs63
-rw-r--r--crates/sellershut/src/main.rs25
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(())
}