Oregami
Repositories/oxedyne/fe2o3

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

1use crate::srv::{
2 cfg::ServerConfig,
3 dev::cfg::DevConfig,
4};
5
6use oxedyne_fe2o3_core::{
7 prelude::*,
8};
9use oxedyne_fe2o3_jdat::{
10 prelude::*,
11 cfg::Config,
12};
13
14use std::{
15 collections::BTreeMap,
16 path::Path,
17};
18
19
20#[derive(Clone, Debug, Eq, PartialEq, FromDatMap, ToDatMap)]
21pub 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
33impl 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
109impl 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
125impl 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}