use std::collections::HashMap;
use std::path::Path;
use serde::Deserialize;
use super::ConfigError;
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TomlConfig {
#[serde(default)]
pub webhook: WebhookSection,
#[serde(default)]
pub filter: FilterSection,
#[serde(default)]
pub monitor: MonitorSection,
#[serde(default)]
pub retry: RetrySection,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WebhookSection {
pub url: Option<String>,
pub ip_version: Option<String>,
pub method: Option<String>,
#[serde(default)]
pub headers: HashMap<String, String>,
pub bearer: Option<String>,
pub body_template: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FilterSection {
#[serde(default)]
pub include: Vec<String>,
#[serde(default)]
pub exclude: Vec<String>,
#[serde(default)]
pub exclude_virtual: bool,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MonitorSection {
pub poll_interval: Option<u64>,
#[serde(default)]
pub poll_only: bool,
pub state_file: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RetrySection {
pub max_attempts: Option<u32>,
pub initial_delay: Option<u64>,
pub max_delay: Option<u64>,
pub multiplier: Option<f64>,
}
impl TomlConfig {
pub fn load(path: &Path) -> Result<Self, ConfigError> {
let content = std::fs::read_to_string(path).map_err(|e| ConfigError::FileRead {
path: path.to_path_buf(),
source: e,
})?;
Self::parse(&content)
}
pub fn parse(content: &str) -> Result<Self, ConfigError> {
toml::from_str(content).map_err(ConfigError::from)
}
}
#[must_use]
pub fn default_config_template() -> String {
r#"# DDNS-A Configuration File
# Documentation: https://github.com/doraemonkeys/ddns-a
[webhook]
# Webhook URL (required)
# url = "https://api.example.com/ddns"
# IP version to monitor (required)
# Accepted values: "ipv4"/"v4"/"4", "ipv6"/"v6"/"6", or "both"/"all"/"dual"
# ip_version = "both"
# HTTP method (default: POST, can be overridden by --method CLI flag)
# method = "POST"
# HTTP headers
# [webhook.headers]
# X-Custom-Header = "value"
# Bearer token for Authorization header
# bearer = "your-token-here"
# Handlebars body template
# Available variables: {{adapter}}, {{address}}, {{timestamp}}, {{kind}}
# body_template = '{"ip": "{{address}}", "adapter": "{{adapter}}"}'
[filter]
# Regex patterns for adapters to include (empty = all)
# Note: CLI patterns REPLACE these entirely (not merged)
# include = ["^eth", "^Ethernet"]
# Regex patterns for adapters to exclude
# Note: CLI patterns REPLACE these entirely (not merged)
# exclude = ["^Docker", "^vEthernet"]
# Exclude virtual adapters (VMware, VirtualBox, Hyper-V, etc.)
exclude_virtual = true
[monitor]
# Polling interval in seconds (default: 60)
poll_interval = 60
# Disable API event listening, use polling only
# poll_only = false
# Path to state file for detecting changes across restarts
# If set, the program will compare current IP addresses with the saved state
# and trigger webhooks for any changes detected during the program restart
# state_file = "ddns-a-state.json"
[retry]
# Maximum number of retry attempts (default: 3)
# max_attempts = 3
# Initial retry delay in seconds (default: 5)
# initial_delay = 5
# Maximum retry delay in seconds (default: 60)
# max_delay = 60
# Backoff multiplier (default: 2.0)
# multiplier = 2.0
"#
.to_string()
}