1use std::path::Path;
26use hashbrown::{HashMap, HashSet};
27use serde_derive::{Serialize, Deserialize};
28use serde_json::Value;
29use toml;
30
31use crate::types::TypeDesc;
32
33
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35#[serde(rename_all = "lowercase")]
36pub enum Visibility {
37 None,
38 User,
39 Advanced,
40 Expert,
41}
42
43impl Default for Visibility {
44 fn default() -> Self { Visibility::User }
45}
46
47#[derive(Deserialize, Debug)]
48pub struct ServerConfig {
49 #[serde(skip)] pub equipment_id: String,
51 pub description: String,
52 pub modules: HashMap<String, ModuleConfig>,
53}
54
55#[derive(Deserialize, Debug, Clone)]
56pub struct ModuleConfig {
57 pub class: String,
58 pub description: String,
59 pub group: Option<String>,
60 #[serde(default)]
61 pub parameters: HashMap<String, Value>,
62 #[serde(default)]
63 pub visibility: Visibility,
64}
65
66
67pub fn load_config(filename: impl AsRef<Path>) -> Result<ServerConfig, String> {
68 let data = std::fs::read(&filename).map_err(|e| e.to_string())?;
69 let mut obj: ServerConfig = toml::from_slice(&data).map_err(|e| e.to_string())?;
70 obj.equipment_id = filename.as_ref()
71 .file_stem()
72 .map_or("unknown".into(), |s| s.to_string_lossy().into_owned());
73
74 let mut lc_names = HashSet::new();
76 let mut lc_groups = HashSet::new();
77 for modcfg in obj.modules.values() {
78 if let Some(group) = modcfg.group.as_ref() {
79 lc_groups.insert(group.to_string());
80 }
81 }
82 for name in obj.modules.keys() {
83 let lc_name = name.to_lowercase();
84 if lc_groups.contains(&lc_name) || !lc_names.insert(lc_name) {
85 return Err(format!("module name {} is not unique amoung modules and groups", name))
86 }
87 }
88
89 Ok(obj)
92}
93
94
95impl ModuleConfig {
97 pub fn extract_param<T: TypeDesc>(&self, param: &str, td: &T) -> Option<T::Repr> {
98 self.parameters.get(param).and_then(|v| td.from_json(v).ok())
99 }
100}