1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4use serde::Deserialize;
5
6#[derive(Deserialize, Debug, Clone)]
9pub struct AgentConfig {
10 pub agent: AgentSection,
11 pub log: LogSection,
12 #[serde(default)]
13 pub inventory: InventorySection,
14}
15
16#[derive(Deserialize, Debug, Clone)]
17pub struct AgentSection {
18 pub id: String,
19 pub nats_url: String,
20 #[serde(default)]
27 pub groups: Vec<String>,
28}
29
30#[derive(Deserialize, Debug, Clone)]
31pub struct LogSection {
32 pub path: String,
33 pub level: String,
34}
35
36#[derive(Deserialize, Debug, Clone)]
37pub struct InventorySection {
38 #[serde(default = "default_hw_interval")]
39 pub hw_interval: String,
40 #[serde(default = "default_jitter")]
41 pub jitter: String,
42 #[serde(default = "default_enabled")]
43 pub enabled: bool,
44}
45
46impl Default for InventorySection {
47 fn default() -> Self {
48 Self {
49 hw_interval: default_hw_interval(),
50 jitter: default_jitter(),
51 enabled: default_enabled(),
52 }
53 }
54}
55
56fn default_hw_interval() -> String {
57 "24h".into()
58}
59fn default_jitter() -> String {
60 "10m".into()
61}
62fn default_enabled() -> bool {
63 true
64}
65
66#[derive(Deserialize, Debug, Clone)]
69pub struct BackendConfig {
70 pub server: ServerSection,
71 pub nats: NatsSection,
72 pub db: DbSection,
73 pub log: LogSection,
74}
75
76#[derive(Deserialize, Debug, Clone)]
77pub struct ServerSection {
78 pub bind: String,
79}
80
81#[derive(Deserialize, Debug, Clone)]
82pub struct NatsSection {
83 pub url: String,
84}
85
86#[derive(Deserialize, Debug, Clone)]
87pub struct DbSection {
88 pub sqlite_path: String,
89}
90
91fn load_typed<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {
94 let mut engine = teravars::Engine::new();
95 let ctx = teravars::system_context();
96 let paths: Vec<PathBuf> = vec![path.to_path_buf()];
97 let merged = teravars::load_merged(&paths, &mut engine, &ctx)
98 .with_context(|| format!("teravars load_merged: {path:?}"))?;
99 let cfg: T = toml::Value::Table(merged.config)
100 .try_into()
101 .with_context(|| format!("decode config from {path:?}"))?;
102 Ok(cfg)
103}
104
105pub fn load_agent_config(path: &Path) -> Result<AgentConfig> {
106 load_typed(path)
107}
108
109pub fn load_backend_config(path: &Path) -> Result<BackendConfig> {
110 load_typed(path)
111}