infrastore_server/
config.rs1use std::path::Path;
2use std::path::PathBuf;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
7pub struct ServerConfig {
8 pub server: ServerSection,
9 pub data: DataSection,
10 #[serde(default)]
11 pub authentication: AuthSection,
12}
13
14#[derive(Debug, Clone, Deserialize, Serialize)]
15pub struct ServerSection {
16 pub host: String,
17 pub port: u16,
18}
19
20#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct DataSection {
22 pub files: Vec<PathBuf>,
25}
26
27#[derive(Debug, Clone, Deserialize, Serialize)]
28pub struct AuthSection {
29 #[serde(default = "default_auth_method")]
31 pub method: String,
32
33 #[serde(default)]
37 pub keys: Vec<String>,
38}
39
40impl Default for AuthSection {
44 fn default() -> Self {
45 Self {
46 method: default_auth_method(),
47 keys: Vec::new(),
48 }
49 }
50}
51
52fn default_auth_method() -> String {
53 "none".into()
54}
55
56impl AuthSection {
57 pub fn validate(&self) -> Result<(), String> {
61 match self.method.as_str() {
62 "none" => Ok(()),
63 "api_key" => {
64 if self.keys.is_empty() {
65 Err(
66 "authentication.method = \"api_key\" requires at least one entry in keys"
67 .into(),
68 )
69 } else {
70 Ok(())
71 }
72 }
73 other => Err(format!("unsupported authentication.method: {other}")),
74 }
75 }
76}
77
78impl ServerConfig {
79 pub fn load(path: &Path) -> Result<Self, ConfigError> {
80 let s = std::fs::read_to_string(path).map_err(ConfigError::Io)?;
81 toml::from_str(&s).map_err(ConfigError::Parse)
82 }
83}
84
85#[derive(Debug, thiserror::Error)]
86pub enum ConfigError {
87 #[error("io error: {0}")]
88 Io(#[from] std::io::Error),
89 #[error("parse error: {0}")]
90 Parse(#[from] toml::de::Error),
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 const BASE: &str = r#"
98[server]
99host = "127.0.0.1"
100port = 50051
101
102[data]
103files = ["store.nc"]
104"#;
105
106 #[test]
107 fn omitted_authentication_section_defaults_to_none() {
108 let cfg: ServerConfig = toml::from_str(BASE).unwrap();
109 assert_eq!(cfg.authentication.method, "none");
110 cfg.authentication.validate().unwrap();
111 }
112
113 #[test]
114 fn authentication_section_without_method_defaults_to_none() {
115 let cfg: ServerConfig = toml::from_str(&format!("{BASE}\n[authentication]\n")).unwrap();
116 assert_eq!(cfg.authentication.method, "none");
117 cfg.authentication.validate().unwrap();
118 }
119
120 #[test]
121 fn api_key_without_keys_is_rejected() {
122 let cfg: ServerConfig =
123 toml::from_str(&format!("{BASE}\n[authentication]\nmethod = \"api_key\"\n")).unwrap();
124 assert!(cfg.authentication.validate().is_err());
125 }
126}