1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
use std::net::SocketAddr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct Config {
pub listen_addr: SocketAddr,
pub jwt_secret: String,
pub database_url: String,
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigBuilder {
listen_addr: Option<String>,
jwt_secret: Option<String>,
database_url: Option<String>,
}
impl ConfigBuilder {
pub fn file(self) -> Result<Self, toml::de::Error> {
let file = std::env::args()
.nth(1)
.unwrap_or("/etc/unnamed_server.toml".to_string());
match std::fs::read_to_string(file) {
Ok(s) => Ok(self.merge(toml::from_str(&s)?)),
Err(err) => {
tracing::warn!("Error reading config file: {err}");
Ok(self)
}
}
}
pub fn env(self) -> Self {
let _ = dotenvy::dotenv();
self.merge(Self {
listen_addr: std::env::var("LISTEN_ADDR").ok(),
jwt_secret: std::env::var("JWT_SECRET").ok(),
database_url: std::env::var("DATABASE_URL").ok(),
})
}
/// Merge self with other, overwriting any existing values on self with other's.
fn merge(self, other: Self) -> Self {
Self {
listen_addr: other.listen_addr.or(self.listen_addr),
jwt_secret: other.jwt_secret.or(self.jwt_secret),
database_url: other.database_url.or(self.database_url),
}
}
pub fn build(self) -> Result<Config, String> {
let err = |s: &str| format!("Config error: missing value: {s}");
Ok(Config {
listen_addr: self
.listen_addr
.and_then(|s| s.parse().ok())
.ok_or_else(|| err("listen_addr"))?,
jwt_secret: self.jwt_secret.ok_or_else(|| err("jwt_secret"))?,
database_url: self.database_url.ok_or_else(|| err("database_url"))?,
})
}
}
impl Default for ConfigBuilder {
fn default() -> Self {
Self {
listen_addr: Some("127.0.0.1:30000".to_string()),
jwt_secret: None,
database_url: None,
}
}
}
|