Skip to main content

dbrest_core/config/
error.rs

1//! Configuration error types
2
3use std::fmt;
4use std::num::ParseIntError;
5
6/// Errors that can occur during configuration loading and parsing
7#[derive(Debug)]
8pub enum ConfigError {
9    /// I/O error reading config file
10    Io(std::io::Error),
11
12    /// Parse error (invalid syntax)
13    Parse {
14        line: Option<usize>,
15        message: String,
16    },
17
18    /// Invalid value for a config key
19    InvalidValue {
20        key: String,
21        value: String,
22        expected: Option<String>,
23    },
24
25    /// Invalid boolean value
26    InvalidBool(String),
27
28    /// Invalid integer value
29    InvalidInt { key: String, error: ParseIntError },
30
31    /// Invalid JSPath expression for JWT role claim
32    InvalidJsPath(String),
33
34    /// Base64 decoding error
35    Base64(base64::DecodeError),
36
37    /// UTF-8 decoding error
38    Utf8(std::string::FromUtf8Error),
39
40    /// Validation error
41    Validation(String),
42}
43
44impl fmt::Display for ConfigError {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        match self {
47            ConfigError::Io(err) => write!(f, "Config file I/O error: {}", err),
48            ConfigError::Parse { line, message } => {
49                if let Some(line_num) = line {
50                    write!(f, "Config parse error at line {}: {}", line_num, message)
51                } else {
52                    write!(f, "Config parse error: {}", message)
53                }
54            }
55            ConfigError::InvalidValue {
56                key,
57                value,
58                expected,
59            } => {
60                if let Some(exp) = expected {
61                    write!(
62                        f,
63                        "Invalid value '{}' for config key '{}', expected: {}",
64                        value, key, exp
65                    )
66                } else {
67                    write!(f, "Invalid value '{}' for config key '{}'", value, key)
68                }
69            }
70            ConfigError::InvalidBool(value) => {
71                write!(
72                    f,
73                    "Invalid boolean value '{}', expected: true/false/yes/no/on/off/1/0",
74                    value
75                )
76            }
77            ConfigError::InvalidInt { key, error } => {
78                write!(f, "Invalid integer for '{}': {}", key, error)
79            }
80            ConfigError::InvalidJsPath(msg) => {
81                write!(f, "Invalid JWT role claim path: {}", msg)
82            }
83            ConfigError::Base64(err) => write!(f, "Base64 decode error: {}", err),
84            ConfigError::Utf8(err) => write!(f, "UTF-8 decode error: {}", err),
85            ConfigError::Validation(msg) => write!(f, "Config validation error: {}", msg),
86        }
87    }
88}
89
90impl std::error::Error for ConfigError {
91    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
92        match self {
93            ConfigError::Io(err) => Some(err),
94            ConfigError::InvalidInt { error, .. } => Some(error),
95            ConfigError::Base64(err) => Some(err),
96            ConfigError::Utf8(err) => Some(err),
97            _ => None,
98        }
99    }
100}
101
102impl From<std::io::Error> for ConfigError {
103    fn from(err: std::io::Error) -> Self {
104        ConfigError::Io(err)
105    }
106}
107
108impl From<ParseIntError> for ConfigError {
109    fn from(err: ParseIntError) -> Self {
110        ConfigError::InvalidInt {
111            key: "unknown".to_string(),
112            error: err,
113        }
114    }
115}
116
117impl From<base64::DecodeError> for ConfigError {
118    fn from(err: base64::DecodeError) -> Self {
119        ConfigError::Base64(err)
120    }
121}
122
123impl From<std::string::FromUtf8Error> for ConfigError {
124    fn from(err: std::string::FromUtf8Error) -> Self {
125        ConfigError::Utf8(err)
126    }
127}
128
129/// Convert ConfigError to the main Error type
130impl From<ConfigError> for crate::error::Error {
131    fn from(err: ConfigError) -> Self {
132        crate::error::Error::InvalidConfig {
133            message: err.to_string(),
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_config_error_display() {
144        let err = ConfigError::InvalidBool("maybe".to_string());
145        assert!(err.to_string().contains("maybe"));
146
147        let err = ConfigError::Validation("test error".to_string());
148        assert!(err.to_string().contains("test error"));
149    }
150
151    #[test]
152    fn test_config_error_parse_with_line() {
153        let err = ConfigError::Parse {
154            line: Some(42),
155            message: "unexpected token".to_string(),
156        };
157        let msg = err.to_string();
158        assert!(msg.contains("42"));
159        assert!(msg.contains("unexpected token"));
160    }
161}