aiscript_runtime/config/
mod.rs1use std::{env, fmt::Display, fs, ops::Deref, path::Path, sync::OnceLock};
2
3use auth::AuthConfig;
4use serde::Deserialize;
5
6use db::DatabaseConfig;
7pub use sso::{SsoConfig, get_sso_fields};
8
9mod auth;
10mod db;
11mod sso;
12#[cfg(test)]
13mod tests;
14
15static CONFIG: OnceLock<Config> = OnceLock::new();
16
17#[derive(Debug, Clone, Deserialize)]
19#[serde(from = "String")]
20pub struct EnvString(String);
21
22impl From<String> for EnvString {
23 fn from(s: String) -> Self {
24 if let Some(env_key) = s.strip_prefix('$') {
25 match env::var(env_key) {
26 Ok(val) => EnvString(val),
27 Err(_) => {
28 EnvString(s)
31 }
32 }
33 } else {
34 EnvString(s)
35 }
36 }
37}
38
39impl Display for EnvString {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "{}", self.0)
42 }
43}
44
45impl From<EnvString> for String {
46 fn from(s: EnvString) -> Self {
47 s.0
48 }
49}
50
51impl Deref for EnvString {
52 type Target = String;
53
54 fn deref(&self) -> &Self::Target {
55 &self.0
56 }
57}
58
59impl AsRef<str> for EnvString {
60 fn as_ref(&self) -> &str {
61 &self.0
62 }
63}
64
65#[derive(Debug, Deserialize, Default)]
66pub struct Config {
67 #[serde(default)]
68 pub database: DatabaseConfig,
69 #[serde(default)]
70 pub apidoc: ApiDocConfig,
71 #[serde(default)]
72 pub auth: AuthConfig,
73 #[serde(default)]
74 pub sso: SsoConfig,
75}
76
77#[derive(Debug, Deserialize, Default)]
78#[serde(rename_all = "lowercase")]
79pub enum ApiDocType {
80 Swagger,
81 #[default]
82 Redoc,
83}
84
85#[derive(Debug, Deserialize)]
86pub struct ApiDocConfig {
87 pub enabled: bool,
88 #[serde(rename = "type", default)]
89 pub doc_type: ApiDocType,
90 #[serde(default = "default_path")]
91 pub path: String,
92}
93
94fn default_path() -> String {
95 "/doc".to_string()
96}
97
98impl Default for ApiDocConfig {
99 fn default() -> Self {
100 Self {
101 enabled: true,
102 doc_type: ApiDocType::default(),
103 path: default_path(),
104 }
105 }
106}
107
108impl Config {
109 fn new(source: impl AsRef<Path>) -> Result<Self, Box<dyn std::error::Error>> {
110 let path = source.as_ref();
111 if path.exists() {
112 let content = fs::read_to_string(path)?;
113 Ok(toml::from_str(&content)?)
114 } else {
115 Ok(Config::default())
116 }
117 }
118
119 pub fn load(path: &str) -> &Config {
120 CONFIG.get_or_init(|| {
121 Config::new(path).unwrap_or_else(|e| {
122 eprintln!("Error loading config file: {}", e);
123 Config::default()
124 })
125 })
126 }
127
128 pub fn get() -> &'static Config {
129 CONFIG.get().expect("Config not initialized")
130 }
131}