oxedyne/fe2o3/fe2o3_steel/src/app/cfg.rs
5.3 KiB, 14 runs
created by r1870400018:941, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use crate::srv::{ |
| 2 | cfg::ServerConfig, |
| 3 | dev::cfg::DevConfig, |
| 4 | }; |
| 5 | |
| 6 | use oxedyne_fe2o3_core::{ |
| 7 | prelude::*, |
| 8 | }; |
| 9 | use oxedyne_fe2o3_jdat::{ |
| 10 | prelude::*, |
| 11 | cfg::Config, |
| 12 | }; |
| 13 | |
| 14 | use std::{ |
| 15 | collections::BTreeMap, |
| 16 | path::Path, |
| 17 | }; |
| 18 | |
| 19 | |
| 20 | #[derive(Clone, Debug, Eq, PartialEq, FromDatMap, ToDatMap)] |
| 21 | pub struct AppConfig { |
| 22 | pub app_root: String, |
| 23 | pub app_name: String, |
| 24 | pub app_human_name: String, |
| 25 | pub app_description: String, |
| 26 | pub app_log_level: String, |
| 27 | pub kdf_name: String, |
| 28 | pub enc_name: String, |
| 29 | pub server_cfg: DaticleMap, |
| 30 | pub dev_cfg: DaticleMap, |
| 31 | } |
| 32 | |
| 33 | impl Config for AppConfig { |
| 34 | /// Validates and resolves the app_root path. Special values: |
| 35 | /// - "current" (or "here", "auto", "."): Use current working directory |
| 36 | /// - "env": Read from STEEL_APP_ROOT environment variable |
| 37 | /// - "~/path": Expand home directory |
| 38 | /// - "./path": Relative to current directory |
| 39 | /// - "/path": Absolute path (used as-is) |
| 40 | fn check_and_fix(&mut self) -> Outcome<()> { |
| 41 | self.app_root = match self.app_root.as_str() { |
| 42 | "current" | "here" | "auto" | "." => { |
| 43 | res!(std::env::current_dir()) |
| 44 | .to_string_lossy() |
| 45 | .to_string() |
| 46 | } |
| 47 | "env" => { |
| 48 | match std::env::var("STEEL_APP_ROOT") { |
| 49 | Ok(path) => path, |
| 50 | Err(_) => return Err(err!( |
| 51 | "STEEL_APP_ROOT environment variable not set when app_root is 'env'."; |
| 52 | Configuration, Missing, Invalid)), |
| 53 | } |
| 54 | } |
| 55 | path if path.starts_with("~/") => { |
| 56 | let home = match std::env::var("HOME") { |
| 57 | Ok(home) => home, |
| 58 | Err(_) => return Err(err!( |
| 59 | "HOME environment variable not set for path expansion."; |
| 60 | Configuration, Missing, Invalid)), |
| 61 | }; |
| 62 | fmt!("{}{}", home, &path[1..]) |
| 63 | } |
| 64 | path if path.starts_with("./") => { |
| 65 | let cwd = res!(std::env::current_dir()); |
| 66 | cwd.join(&path[2..]).to_string_lossy().to_string() |
| 67 | } |
| 68 | path => path.to_string(), |
| 69 | }; |
| 70 | |
| 71 | let app_root_path = Path::new(&self.app_root); |
| 72 | |
| 73 | if let Some(parent) = app_root_path.parent() { |
| 74 | if !parent.exists() { |
| 75 | return Err(err!( |
| 76 | "Parent directory '{:?}' does not exist for app_root '{:?}'. \ |
| 77 | You may need to create the parent directories first.", |
| 78 | parent, app_root_path; |
| 79 | Path, Missing)); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | match std::fs::create_dir_all(app_root_path) { |
| 84 | Ok(()) => (), |
| 85 | Err(e) => { |
| 86 | let suggestion = match e.kind() { |
| 87 | std::io::ErrorKind::PermissionDenied => fmt!( |
| 88 | "You may not have the necessary permissions to create \ |
| 89 | directories at '{:?}'. Try running with elevated privileges \ |
| 90 | or choose a different location.", app_root_path), |
| 91 | std::io::ErrorKind::NotFound => fmt!( |
| 92 | "The path '{:?}' cannot be created. Check that all parent \ |
| 93 | directories exist and are accessible.", app_root_path), |
| 94 | std::io::ErrorKind::AlreadyExists => fmt!( |
| 95 | "A file already exists at '{:?}' preventing directory creation.", |
| 96 | app_root_path), |
| 97 | _ => fmt!( |
| 98 | "Unable to create directory at '{:?}'. Check path validity \ |
| 99 | and permissions.", app_root_path), |
| 100 | }; |
| 101 | return Err(err!(e, "{}", suggestion; IO, Path, Create)); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | Ok(()) |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | impl Default for AppConfig { |
| 110 | fn default() -> Self { |
| 111 | Self { |
| 112 | app_root: String::new(), |
| 113 | app_name: fmt!("steel"), |
| 114 | app_human_name: fmt!("Steel Server"), |
| 115 | app_description: fmt!("A Hematite Steel Server app."), |
| 116 | app_log_level: fmt!("debug"), |
| 117 | kdf_name: fmt!("Argon2id_v0x13"), |
| 118 | enc_name: fmt!("AES-256-GCM"), |
| 119 | server_cfg: DaticleMap::new(), |
| 120 | dev_cfg: DaticleMap::new(), |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | impl AppConfig { |
| 126 | |
| 127 | pub fn new() -> Outcome<Self> { |
| 128 | let mut cfg = Self::default(); |
| 129 | cfg.app_root = fmt!("{}", res!(std::env::current_dir()).display()); |
| 130 | cfg.server_cfg = try_extract_dat!( |
| 131 | ServerConfig::to_datmap(ServerConfig::default()), |
| 132 | Map, |
| 133 | ); |
| 134 | cfg.dev_cfg = try_extract_dat!( |
| 135 | DevConfig::to_datmap(DevConfig::default()), |
| 136 | Map, |
| 137 | ); |
| 138 | Ok(cfg) |
| 139 | } |
| 140 | |
| 141 | pub fn server_log_level(&self) -> Outcome<(LogLevel, String)> { |
| 142 | let level_str = if let Some(dat) = self.server_cfg.get(&dat!("log_level")) { |
| 143 | try_extract_dat!(dat, Str) |
| 144 | } else { |
| 145 | return Err(err!( |
| 146 | "Log level key not found in server configuration: {:?}.", |
| 147 | self.server_cfg; |
| 148 | Configuration, Missing, Key)); |
| 149 | }; |
| 150 | let level = res!(LogLevel::from_str(&level_str)); |
| 151 | Ok((level, level_str.clone())) |
| 152 | } |
| 153 | } |