Skip to main content

ch_config/config/
error.rs

1use std::io;
2use std::path::PathBuf;
3
4use thiserror::Error;
5
6/// Error returned while resolving or validating configuration.
7#[derive(Debug, Error)]
8pub enum ConfigError {
9    /// A parsed or overridden value violates a configuration invariant.
10    #[error("invalid configuration field `{field}`: {message}")]
11    Validation {
12        /// Stable dotted field path.
13        field: &'static str,
14        /// Value-independent validation explanation.
15        message: &'static str,
16    },
17
18    /// An explicit environment file could not be loaded.
19    #[error("failed to load environment file `{path}`")]
20    Environment {
21        /// File that failed to load.
22        path: PathBuf,
23        /// Sanitized dotenv failure that never retains a parsed line or value.
24        #[source]
25        source: EnvironmentFileError,
26    },
27
28    /// The immutable process configuration was already initialized from different files.
29    #[error("configuration files have already been loaded for this process")]
30    EnvironmentAlreadyLoaded,
31
32    /// A process variable and its `_FILE` companion were both supplied.
33    #[error("configuration variable `{variable}` conflicts with `{variable}_FILE`")]
34    VariableConflict {
35        /// Base environment-variable name.
36        variable: &'static str,
37    },
38
39    /// A requested raw environment variable was not supplied.
40    #[error("configuration variable `{variable}` is not set")]
41    VariableMissing {
42        /// Environment-variable name.
43        variable: String,
44    },
45
46    /// A process variable is not valid Unicode.
47    #[error("configuration variable `{variable}` must be valid Unicode")]
48    VariableUnicode {
49        /// Environment-variable name.
50        variable: String,
51    },
52
53    /// A configured value is empty or cannot be parsed into its typed field.
54    #[error("configuration variable `{variable}` {message}")]
55    VariableValue {
56        /// Environment-variable name.
57        variable: &'static str,
58        /// Value-independent explanation.
59        message: &'static str,
60    },
61
62    /// A file selected through a `_FILE` variable could not be read.
63    #[error("failed to read configuration value selected by `{variable}_FILE`")]
64    VariableFile {
65        /// Base environment-variable name.
66        variable: &'static str,
67        /// Underlying filesystem error.
68        #[source]
69        source: io::Error,
70    },
71}
72
73/// Sanitized cause for an explicitly selected dotenv file.
74#[derive(Debug, Error)]
75pub enum EnvironmentFileError {
76    /// The file could not be read.
77    #[error("environment file could not be read")]
78    Io(#[source] io::Error),
79    /// A dotenv line has invalid syntax at the reported character index.
80    #[error("invalid dotenv syntax at character index {index}")]
81    Syntax {
82        /// Zero-based character index reported within the invalid line.
83        index: usize,
84    },
85    /// A referenced process variable was unavailable or not valid Unicode.
86    #[error("referenced environment variable is unavailable")]
87    Variable,
88    /// A future dotenv error kind was returned by the non-exhaustive API.
89    #[error("environment file could not be loaded")]
90    Other,
91}
92
93impl EnvironmentFileError {
94    pub(crate) fn from_dotenv(error: dotenv::Error) -> Self {
95        match error {
96            dotenv::Error::Parse(error, _) => Self::Syntax {
97                index: error.column().saturating_sub(1),
98            },
99            dotenv::Error::Io(source, _) => Self::Io(source),
100            dotenv::Error::NotPresent(_)
101            | dotenv::Error::NotUnicode(_, _)
102            | dotenv::Error::NotUnicodeName(_) => Self::Variable,
103            _ => Self::Other,
104        }
105    }
106}
107
108impl ConfigError {
109    pub(crate) const fn validation(field: &'static str, message: &'static str) -> Self {
110        Self::Validation { field, message }
111    }
112
113    pub(crate) fn environment(path: PathBuf, error: dotenv::Error) -> Self {
114        Self::Environment {
115            path,
116            source: EnvironmentFileError::from_dotenv(error),
117        }
118    }
119}
120
121/// Result type used by this crate.
122pub type Result<T> = std::result::Result<T, ConfigError>;