ddns_a/config/
error.rs

1//! Error types for configuration parsing and validation.
2
3use std::path::PathBuf;
4
5use thiserror::Error;
6
7/// Error type for configuration operations.
8///
9/// Covers errors from parsing, validation, and file operations.
10#[derive(Debug, Error)]
11pub enum ConfigError {
12    /// Failed to read the configuration file.
13    #[error("Failed to read config file '{}': {source}", path.display())]
14    FileRead {
15        /// Path to the config file
16        path: PathBuf,
17        /// Underlying I/O error
18        #[source]
19        source: std::io::Error,
20    },
21
22    /// Failed to parse the TOML configuration.
23    #[error("Failed to parse TOML config: {0}")]
24    TomlParse(#[from] toml::de::Error),
25
26    /// Failed to write configuration file (for init command).
27    #[error("Failed to write config file '{}': {source}", path.display())]
28    FileWrite {
29        /// Path to the config file
30        path: PathBuf,
31        /// Underlying I/O error
32        #[source]
33        source: std::io::Error,
34    },
35
36    /// Missing required field that must be provided by CLI or config file.
37    #[error("Missing required field: {field}. {hint}")]
38    MissingRequired {
39        /// Name of the missing field
40        field: &'static str,
41        /// Hint for how to provide the value
42        hint: &'static str,
43    },
44
45    /// Invalid URL provided.
46    #[error("Invalid URL '{url}': {reason}")]
47    InvalidUrl {
48        /// The invalid URL string
49        url: String,
50        /// Reason for invalidity
51        reason: String,
52    },
53
54    /// Invalid regex pattern for adapter filtering.
55    #[error("Invalid regex pattern '{pattern}': {source}")]
56    InvalidRegex {
57        /// The invalid pattern
58        pattern: String,
59        /// Underlying regex error
60        #[source]
61        source: regex::Error,
62    },
63
64    /// Invalid duration value (zero or too large).
65    #[error("Invalid duration for {field}: {reason}")]
66    InvalidDuration {
67        /// Name of the field
68        field: &'static str,
69        /// Reason for invalidity
70        reason: String,
71    },
72
73    /// Invalid retry configuration.
74    #[error("Invalid retry configuration: {0}")]
75    InvalidRetry(String),
76
77    /// Invalid HTTP method.
78    #[error("Invalid HTTP method '{0}'")]
79    InvalidMethod(String),
80
81    /// Invalid IP version value.
82    #[error("Invalid IP version '{value}': expected ipv4, ipv6, or both")]
83    InvalidIpVersion {
84        /// The invalid value provided
85        value: String,
86    },
87
88    /// Invalid adapter kind value.
89    #[error("Invalid adapter kind '{value}': expected ethernet, wireless, virtual, or loopback")]
90    InvalidAdapterKind {
91        /// The invalid value provided
92        value: String,
93    },
94
95    /// Invalid change kind value.
96    #[error("Invalid change kind '{value}': expected added, removed, or both")]
97    InvalidChangeKind {
98        /// The invalid value provided
99        value: String,
100    },
101
102    /// Invalid header format.
103    #[error("Invalid header format '{value}': expected 'Key=Value' or 'Key: Value'")]
104    InvalidHeader {
105        /// The invalid header string
106        value: String,
107    },
108
109    /// Invalid header name.
110    #[error("Invalid header name '{name}': {reason}")]
111    InvalidHeaderName {
112        /// The invalid header name
113        name: String,
114        /// Reason for invalidity
115        reason: String,
116    },
117
118    /// Invalid header value.
119    #[error("Invalid header value for '{name}': {reason}")]
120    InvalidHeaderValue {
121        /// The header name
122        name: String,
123        /// Reason for invalidity
124        reason: String,
125    },
126
127    /// Invalid body template (Handlebars syntax error).
128    #[error("Invalid body template: {reason}")]
129    InvalidTemplate {
130        /// Reason for invalidity
131        reason: String,
132    },
133}
134
135/// Well-known field names for `MissingRequired` errors.
136///
137/// Use these constants for compile-time safety when matching field names.
138pub mod field {
139    /// The webhook URL field.
140    pub const URL: &str = "url";
141    /// The IP version field.
142    pub const IP_VERSION: &str = "ip_version";
143}
144
145impl ConfigError {
146    /// Creates a `MissingRequired` error for a required field.
147    #[must_use]
148    pub const fn missing(field: &'static str, hint: &'static str) -> Self {
149        Self::MissingRequired { field, hint }
150    }
151}