use std::path::Path;
use std::sync::Arc;
use config::{Config, Environment, File};
use serde::Deserialize;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum PurwaConfigError {
#[error(transparent)]
Config(#[from] config::ConfigError),
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct AppSection {
pub name: String,
}
impl Default for AppSection {
fn default() -> Self {
Self {
name: "purwa-app".to_string(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct ServerSection {
pub host: String,
pub port: u16,
}
impl Default for ServerSection {
fn default() -> Self {
Self {
host: "0.0.0.0".to_string(),
port: 3000,
}
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(default)]
pub struct DatabaseSection {
pub url: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(default)]
pub struct InertiaSection {
pub asset_version: String,
}
impl Default for InertiaSection {
fn default() -> Self {
Self {
asset_version: "1".to_string(),
}
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(default)]
pub struct AppConfig {
pub app: AppSection,
pub server: ServerSection,
pub database: DatabaseSection,
pub inertia: InertiaSection,
}
impl AppConfig {
pub fn load() -> Result<Arc<Self>, PurwaConfigError> {
Self::load_with_file(None)
}
pub fn load_with_file(purwa_toml: Option<&Path>) -> Result<Arc<Self>, PurwaConfigError> {
dotenvy::dotenv().ok();
let mut builder = Config::builder();
match purwa_toml {
Some(path) => {
builder = builder.add_source(File::from(path).required(true));
}
None => {
builder = builder.add_source(File::with_name("purwa").required(false));
}
}
builder = builder.add_source(
Environment::with_prefix("PURWA")
.separator("__")
.try_parsing(true),
);
let cfg = builder.build()?;
let app: AppConfig = cfg.try_deserialize()?;
Ok(Arc::new(app))
}
pub fn database_url(&self) -> Option<String> {
if let Some(ref u) = self.database.url {
let t = u.trim();
if !t.is_empty() {
return Some(t.to_string());
}
}
std::env::var("DATABASE_URL")
.ok()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
}
}