cfgmatic_source/domain/
error.rs1use std::path::PathBuf;
9
10#[derive(Debug, thiserror::Error)]
15pub enum SourceError {
16 #[error("Source not found: {0}")]
18 NotFound(String),
19
20 #[error("Failed to read source: {0}")]
22 ReadFailed(String),
23
24 #[error("Parse error in {format} at {path}: {message}")]
26 ParseFailed {
27 path: String,
29 format: String,
31 message: String,
33 },
34
35 #[error("Invalid format: expected {expected}, found {found}")]
37 InvalidFormat {
38 expected: String,
40 found: String,
42 },
43
44 #[error("IO error: {0}")]
46 Io(String),
47
48 #[error("Environment variable error: {0}")]
50 EnvVar(String),
51
52 #[error("Network error: {0}")]
54 Network(String),
55
56 #[error("Serialization error: {0}")]
58 Serialization(String),
59
60 #[error("Invalid path: {0}")]
62 InvalidPath(PathBuf),
63
64 #[error("Unsupported operation: {0}")]
66 Unsupported(String),
67
68 #[error("Validation failed: {0}")]
70 Validation(String),
71
72 #[error("{0}")]
74 Custom(String),
75}
76
77pub type Result<T> = std::result::Result<T, SourceError>;
79
80impl SourceError {
81 #[must_use]
83 pub fn not_found(source: &str) -> Self {
84 Self::NotFound(source.to_string())
85 }
86
87 #[must_use]
89 pub fn read_failed(source: &str) -> Self {
90 Self::ReadFailed(source.to_string())
91 }
92
93 #[must_use]
95 pub fn parse_failed(path: &str, format: &str, message: &str) -> Self {
96 Self::ParseFailed {
97 path: path.to_string(),
98 format: format.to_string(),
99 message: message.to_string(),
100 }
101 }
102
103 #[must_use]
105 pub fn invalid_format(expected: &str, found: &str) -> Self {
106 Self::InvalidFormat {
107 expected: expected.to_string(),
108 found: found.to_string(),
109 }
110 }
111
112 #[must_use]
114 pub fn io(message: &str) -> Self {
115 Self::Io(message.to_string())
116 }
117
118 #[must_use]
120 pub fn env_var(message: &str) -> Self {
121 Self::EnvVar(message.to_string())
122 }
123
124 #[must_use]
126 pub fn network(message: &str) -> Self {
127 Self::Network(message.to_string())
128 }
129
130 #[must_use]
132 pub fn serialization(message: &str) -> Self {
133 Self::Serialization(message.to_string())
134 }
135
136 #[must_use]
138 pub const fn invalid_path(path: PathBuf) -> Self {
139 Self::InvalidPath(path)
140 }
141
142 #[must_use]
144 pub fn unsupported(operation: &str) -> Self {
145 Self::Unsupported(operation.to_string())
146 }
147
148 #[must_use]
150 pub fn validation(message: &str) -> Self {
151 Self::Validation(message.to_string())
152 }
153
154 #[must_use]
156 pub fn custom(message: &str) -> Self {
157 Self::Custom(message.to_string())
158 }
159
160 #[must_use]
162 pub const fn is_not_found(&self) -> bool {
163 matches!(self, Self::NotFound(_))
164 }
165
166 #[must_use]
168 pub const fn is_parse_failed(&self) -> bool {
169 matches!(self, Self::ParseFailed { .. })
170 }
171
172 #[must_use]
174 pub const fn is_network(&self) -> bool {
175 matches!(self, Self::Network(_))
176 }
177
178 #[must_use]
180 pub const fn is_io(&self) -> bool {
181 matches!(self, Self::Io(_))
182 }
183}
184
185impl From<std::io::Error> for SourceError {
186 fn from(err: std::io::Error) -> Self {
187 Self::Io(err.to_string())
188 }
189}
190
191#[cfg(feature = "json")]
192impl From<serde_json::Error> for SourceError {
193 fn from(err: serde_json::Error) -> Self {
194 Self::Serialization(err.to_string())
195 }
196}
197
198impl From<std::env::VarError> for SourceError {
199 fn from(err: std::env::VarError) -> Self {
200 Self::EnvVar(err.to_string())
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use std::path::PathBuf;
208
209 #[test]
210 fn test_error_not_found() {
211 let err = SourceError::not_found("config.toml");
212 assert!(err.is_not_found());
213 assert!(err.to_string().contains("not found"));
214 }
215
216 #[test]
217 fn test_error_parse_failed() {
218 let err = SourceError::parse_failed("/etc/config.toml", "toml", "invalid syntax");
219 assert!(err.is_parse_failed());
220 assert!(err.to_string().contains("Parse error"));
221 }
222
223 #[test]
224 fn test_error_invalid_format() {
225 let err = SourceError::invalid_format("toml", "yaml");
226 assert!(err.to_string().contains("Invalid format"));
227 }
228
229 #[test]
230 fn test_error_io() {
231 let err = SourceError::io("permission denied");
232 assert!(err.is_io());
233 }
234
235 #[test]
236 fn test_error_invalid_path() {
237 let path = PathBuf::from("/invalid/path");
238 let err = SourceError::invalid_path(path.clone());
239 assert!(matches!(err, SourceError::InvalidPath(p) if p == path));
240 }
241
242 #[test]
243 fn test_error_from_io() {
244 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
245 let err: SourceError = io_err.into();
246 assert!(err.is_io());
247 }
248
249 #[test]
250 fn test_error_from_var_error() {
251 let var_err = std::env::VarError::NotPresent;
252 let err: SourceError = var_err.into();
253 assert!(matches!(err, SourceError::EnvVar(_)));
254 }
255}