//! Configuration use std::net::{SocketAddr, ToSocketAddrs}; use camino::{Utf8Path, Utf8PathBuf}; use confindent::Confindent; use scurvy::{ Argument, Scurvy, arg, formula::{Boolean, BooleanFormula, Utf8PathFormula}, }; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; use crate::fs::{RequestPath, SafePathBuf}; /// CLI arguments /// /// Structure containing values grabbed off the command line, /// passed in as arguments. Parsed with `scurvy`, so arguments /// are in the `key=value arg=opt` style #[derive(Debug)] pub struct CommandLine { conf: Utf8PathBuf, refresh_sections: bool, } impl CommandLine { pub fn get() -> Self { let args = vec![ arg!(["config", "c"] = "path"), arg!("refresh-sections" = "bool"), ]; let scurv = Scurvy::make(args); let conf = scurv.parse_or("config", Utf8PathFormula, "/etc/splorm.conf".into()); let refresh_sections = *scurv.parse_or("refresh-sections", BooleanFormula, Boolean::No); Self { conf, refresh_sections, } } pub fn conf_path(&self) -> &Utf8Path { &self.conf } pub fn refresh_sections(&self) -> bool { self.refresh_sections } } /// Config values parsed from a file /// /// This structure is not meant to be passed to routes, that role is filled /// by [Settings] /// /// use [Config::get] to create this structure. #[derive(Debug)] pub struct Config { port: u16, settings: Settings, } impl Config { pub fn get(path: &Utf8Path) -> Config { tracing::trace!("loading conf from {path}"); let confi = Confindent::from_file(path).unwrap(); let network = confi.child("Network").unwrap(); let port = network.child_parse("Port").unwrap(); let serving = confi.child("Serving").unwrap(); let webroot: Utf8PathBuf = serving.parse().unwrap(); let hostname = serving.child_owned("Hostname").unwrap(); let uploads = serving.child_parse("Uploads").unwrap(); let templates_sec = serving.child("Templates").unwrap(); let templates: Utf8PathBuf = templates_sec.parse().unwrap(); let default_template = templates_sec.child_owned("Default").unwrap(); let opengraph = serving.child("Opengraph").unwrap(); let og_image = opengraph.child_owned("DefaultImage").unwrap(); let og_image_alt = opengraph.get_delim("DefaultImage/Text", '/').unwrap(); let wikarden_sec = serving.child("Wikarden").unwrap(); let wikarden_root = wikarden_sec.parse().unwrap(); let wikarden_index = wikarden_sec.child_owned("Index").unwrap(); let wikarden_gitname = wikarden_sec.child_owned("GitName").unwrap(); let wikarden_gitemail = wikarden_sec.child_owned("GitEmail").unwrap(); let me_sec = serving.child("Me").unwrap(); let me_email = me_sec.child_owned("Email").unwrap(); let auth_sec = confi.child("Auth").unwrap(); let allow_registration = auth_sec.child_parse("AllowRegistration").unwrap(); let database_path = confi.child_parse("Database").unwrap(); let connectors_sec = confi.child("Connectors").unwrap(); let gatherer_path: Utf8PathBuf = connectors_sec.child_parse("Gatherer").unwrap(); Self { port, settings: Settings { default_template, template_dir: templates, // webroot.join(templates), opengraph: Opengraph { image: og_image, image_alt: og_image_alt.to_owned(), }, hostname, webroot, wikarden_root, wikarden_index, wikarden_gitname, wikarden_gitemail, me_email, allow_registration, database_path, uploads, connectors: Connectors { gatherer_path }, }, } } pub fn listen_address(&self) -> SocketAddr { ("0.0.0.0", self.port) .to_socket_addrs() .unwrap() .next() .unwrap() } pub fn settings(&self) -> &Settings { &self.settings } } /// Long configuration needed by routes. /// /// Settings are values that need to live for a long time. This structure /// should be added to the router as an extension, and be extracted in /// routes with `Extension(settings): Extension` #[derive(Clone, Debug)] pub struct Settings { webroot: Utf8PathBuf, hostname: String, default_template: String, template_dir: Utf8PathBuf, opengraph: Opengraph, wikarden_root: Utf8PathBuf, wikarden_index: String, wikarden_gitname: String, wikarden_gitemail: String, me_email: String, allow_registration: bool, database_path: Utf8PathBuf, uploads: Utf8PathBuf, connectors: Connectors, } impl Settings { pub fn root(&self) -> &Utf8Path { &self.webroot } pub fn hostname(&self) -> &str { &self.hostname } pub fn template_dir(&self) -> &Utf8Path { &self.template_dir } pub fn quark_template_dir(&self) -> Utf8PathBuf { self.template_dir().join("quark") } pub fn default_template_name(&self) -> &str { &self.default_template } pub fn default_template_path(&self) -> SafePathBuf { RequestPath::verify_safe( self.template_dir(), &format!("{}.html", self.default_template), ) .unwrap() .system_path() .to_owned() } pub fn wikarden_root(&self) -> &Utf8Path { &self.wikarden_root } pub fn wikarden_index(&self) -> &str { &self.wikarden_index } pub fn wikarden_gitname(&self) -> &str { &self.wikarden_gitname } pub fn wikarden_gitemail(&self) -> &str { &self.wikarden_gitemail } pub fn me_email(&self) -> &str { &self.me_email } pub fn registration_enabled(&self) -> bool { self.allow_registration } pub fn database_path(&self) -> &Utf8Path { &self.database_path } pub fn opengraph(&self) -> &Opengraph { &self.opengraph } pub fn connectors(&self) -> &Connectors { &self.connectors } } /// Part of [Settings], separate for ease #[derive(Clone, Debug)] pub struct Connectors { gatherer_path: Utf8PathBuf, } impl Connectors { pub fn gatherer_path(&self) -> &Utf8Path { &self.gatherer_path } } /// Used by [`main()`][crate::main]. pub fn create_tracing_subscriber() { tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) .with( EnvFilter::try_from_default_env() .or_else(|_| EnvFilter::try_new("info")) .unwrap(), ) .init(); } #[derive(Clone, Debug)] pub struct Opengraph { image: String, image_alt: String, } impl Opengraph { pub fn default_image(&self) -> &str { &self.image } pub fn default_image_alt(&self) -> &str { &self.image_alt } }