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    /// A process variable and its `_FILE` companion were both supplied.
29    #[error("configuration variable `{variable}` conflicts with `{variable}_FILE`")]
30    VariableConflict {
31        /// Base environment-variable name.
32        variable: &'static str,
33    },
34
35    /// A process variable is not valid Unicode.
36    #[error("configuration variable `{variable}` must be valid Unicode")]
37    VariableUnicode {
38        /// Environment-variable name.
39        variable: String,
40    },
41
42    /// A configured value is empty or cannot be parsed into its typed field.
43    #[error("configuration variable `{variable}` {message}")]
44    VariableValue {
45        /// Environment-variable name.
46        variable: &'static str,
47        /// Value-independent explanation.
48        message: &'static str,
49    },
50
51    /// A file selected through a `_FILE` variable could not be read.
52    #[error("failed to read configuration value selected by `{variable}_FILE`")]
53    VariableFile {
54        /// Base environment-variable name.
55        variable: &'static str,
56        /// Underlying filesystem error.
57        #[source]
58        source: io::Error,
59    },
60}
61
62/// Sanitized cause for an explicitly selected dotenv file.
63#[derive(Debug, Error)]
64pub enum EnvironmentFileError {
65    /// The file could not be read.
66    #[error("environment file could not be read")]
67    Io(#[source] io::Error),
68    /// A dotenv line has invalid syntax at the reported character index.
69    #[error("invalid dotenv syntax at character index {index}")]
70    Syntax {
71        /// Zero-based character index reported within the invalid line.
72        index: usize,
73    },
74    /// A referenced process variable was unavailable or not valid Unicode.
75    #[error("referenced environment variable is unavailable")]
76    Variable,
77    /// A future dotenv error kind was returned by the non-exhaustive API.
78    #[error("environment file could not be loaded")]
79    Other,
80}
81
82impl EnvironmentFileError {
83    pub(crate) fn from_dotenv(error: dotenvy::Error) -> Self {
84        match error {
85            dotenvy::Error::LineParse(_, index) => Self::Syntax { index },
86            dotenvy::Error::Io(source) => Self::Io(source),
87            dotenvy::Error::EnvVar(_) => Self::Variable,
88            _ => Self::Other,
89        }
90    }
91}
92
93impl ConfigError {
94    pub(crate) const fn validation(field: &'static str, message: &'static str) -> Self {
95        Self::Validation { field, message }
96    }
97
98    pub(crate) fn environment(path: PathBuf, error: dotenvy::Error) -> Self {
99        Self::Environment {
100            path,
101            source: EnvironmentFileError::from_dotenv(error),
102        }
103    }
104}
105
106/// Result type used by this crate.
107pub type Result<T> = std::result::Result<T, ConfigError>;