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)]
19 pub inventory: InventorySection,
20}
21
22#[derive(Deserialize, Debug, Clone)]
23pub struct AgentSection {
24 pub id: String,
25 pub nats_url: String,
26 #[serde(default)]
33 pub groups: Vec<String>,
34}
35
36#[derive(Deserialize, Debug, Clone)]
37pub struct LogSection {
38 pub path: String,
39 pub level: String,
40 #[serde(default = "default_keep_days")]
45 pub keep_days: usize,
46}
47
48fn default_keep_days() -> usize {
49 14
50}
51
52#[derive(Deserialize, Debug, Clone)]
53pub struct InventorySection {
54 #[serde(default = "default_hw_interval")]
55 pub hw_interval: String,
56 #[serde(default = "default_jitter")]
57 pub jitter: String,
58 #[serde(default = "default_enabled")]
59 pub enabled: bool,
60}
61
62impl Default for InventorySection {
63 fn default() -> Self {
64 Self {
65 hw_interval: default_hw_interval(),
66 jitter: default_jitter(),
67 enabled: default_enabled(),
68 }
69 }
70}
71
72fn default_hw_interval() -> String {
73 "24h".into()
74}
75fn default_jitter() -> String {
76 "10m".into()
77}
78fn default_enabled() -> bool {
79 true
80}
81
82#[derive(Deserialize, Debug, Clone)]
85pub struct BackendConfig {
86 pub server: ServerSection,
87 pub nats: NatsSection,
88 pub db: DbSection,
89 pub log: LogSection,
90}
91
92#[derive(Deserialize, Debug, Clone)]
93pub struct ServerSection {
94 pub bind: String,
95}
96
97#[derive(Deserialize, Debug, Clone)]
98pub struct NatsSection {
99 pub url: String,
100}
101
102#[derive(Deserialize, Debug, Clone)]
103pub struct DbSection {
104 pub sqlite_path: String,
105}
106
107fn load_typed<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {
110 let mut engine = teravars::Engine::new();
111 let ctx = teravars::system_context();
112 let paths: Vec<PathBuf> = vec![path.to_path_buf()];
113 let merged = teravars::load_merged(&paths, &mut engine, &ctx)
114 .with_context(|| format!("teravars load_merged: {path:?}"))?;
115 let cfg: T = toml::Value::Table(merged.config)
116 .try_into()
117 .with_context(|| format!("decode config from {path:?}"))?;
118 Ok(cfg)
119}
120
121pub fn load_agent_config(path: &Path) -> Result<AgentConfig> {
122 load_typed(path)
123}
124
125pub fn load_backend_config(path: &Path) -> Result<BackendConfig> {
126 load_typed(path)
127}