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