1use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use koi_common::paths;
8use koi_common::persist;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, utoipa::ToSchema)]
12pub struct DnsEntry {
13 pub name: String,
14 pub ip: String,
15 #[serde(default)]
16 pub ttl: Option<u32>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
21pub struct DnsState {
22 #[serde(default)]
23 pub entries: Vec<DnsEntry>,
24}
25
26pub fn dns_state_path() -> PathBuf {
28 paths::koi_state_dir().join("dns.json")
29}
30
31pub fn load_dns_state() -> Result<DnsState, std::io::Error> {
33 let path = dns_state_path();
34 persist::read_json_or_default(&path)
35}
36
37pub fn save_dns_state(state: &DnsState) -> Result<(), std::io::Error> {
39 let path = dns_state_path();
40 persist::write_json_pretty(&path, state)
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn dns_state_round_trip() {
49 let state = DnsState {
50 entries: vec![DnsEntry {
51 name: "grafana.lan".to_string(),
52 ip: "192.168.1.50".to_string(),
53 ttl: Some(60),
54 }],
55 };
56 let json = serde_json::to_string(&state).unwrap();
57 let parsed: DnsState = serde_json::from_str(&json).unwrap();
58 assert_eq!(state, parsed);
59 }
60
61 #[test]
62 fn load_dns_state_missing_returns_default() {
63 let _ = koi_common::test::ensure_data_dir("koi-config-state-tests");
64 let state = load_dns_state().unwrap();
65 assert!(state.entries.is_empty());
66 }
67}