This commit is contained in:
2026-09-07 22:47:34 +03:00
commit 3fcee5ea9a
27 changed files with 1849 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
use camino::Utf8PathBuf;
#[derive(Debug, Clone, clap::Parser)]
#[command(
name = "erplt",
about = "ERP system for European businesses",
long_about = "ERPLT is a lightweight ERP system built for the European market.",
)]
pub struct Cli {
/// Config path
#[arg(long, short='c', default_value="config.toml")]
pub config: Utf8PathBuf,
/// Debug output
#[arg(long, short='d', default_value_t=false)]
pub debug: bool,
}

View File

@@ -0,0 +1,107 @@
use std::time::Duration;
use anyhow::Context;
use camino::Utf8PathBuf;
use serde::de;
pub mod cli;
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct Config {
#[serde(skip)]
pub config_path: Utf8PathBuf,
pub core: ConfigCore,
pub database: ConfigDatabase,
pub web: ConfigWeb,
}
impl Config {
pub(crate) fn parse() -> anyhow::Result<Self> {
if !crate::CLI.config.exists() {
tracing::warn!("Configuration does not exist, creating at {}", crate::CLI.config.canonicalize_utf8()?);
let tml = toml::to_string_pretty(&Config::default()).context("Invalid default config????")?;
std::fs::write(&crate::CLI.config, tml).context("Failed to write default config, check permissions?")?;
}
let text = std::fs::read_to_string(&crate::CLI.config).context("Failed to read config, check permissions?")?;
let mut tml: Self = toml::from_str(&text)?;
tml.config_path = crate::CLI.config.clone();
tml.core.debug = tml.core.debug || crate::CLI.debug;
tracing::info!("Read config at {}", crate::CLI.config.canonicalize_utf8()?);
Ok(tml)
}
pub fn get() -> &'static Self {
crate::CONFIG.get().expect("Config hasnt been read and initialised yet")
}
pub(crate) fn load_to_global(self) {
crate::CONFIG.set(self).expect("Config was already set globaly")
}
}
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct ConfigCore {
pub debug: bool,
}
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum DatabaseType {
#[default]
Postgresql,
}
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct ConfigDatabase {
pub host: String,
pub port: u16,
pub database: String,
pub username: String,
pub password: String,
#[serde(rename="type")]
pub db_type: DatabaseType,
pub max_pool_size: u32,
pub min_idle: u32,
#[serde(with = "humantime_serde")]
pub connection_timeout: Duration,
#[serde(with = "humantime_serde")]
pub idle_timeout: Duration,
#[serde(with = "humantime_serde")]
pub max_lifetime: Duration,
pub test_on_check_out: bool
}
impl ConfigDatabase {
pub fn connect_url(&self) -> String {
format!(
"postgres://{}:{}@{}:{}/{}",
self.username,
self.password,
self.host,
self.port,
self.database,
)
}
pub fn connect_url_redacted(&self) -> String {
format!(
"postgres://{}:***@{}:{}/{}",
self.username,
self.host,
self.port,
self.database,
)
}
}
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
pub struct ConfigWeb {
pub host: String,
pub port: u16,
}

View File

@@ -0,0 +1,20 @@
use std::sync::{LazyLock, OnceLock};
use clap::Parser;
mod config;
pub use config::Config;
static CLI: LazyLock<config::cli::Cli> = LazyLock::new(config::cli::Cli::parse);
static CONFIG: OnceLock<config::Config> = OnceLock::new();
pub fn init() -> anyhow::Result<()> {
let _ = &*CLI;
config::Config::parse()?.load_to_global();
Ok(())
}
// helper function for faster access
pub fn get() -> &'static Config {
Config::get()
}