use std::error::Error;
use std::fmt;
pub type ParserError = Box<dyn Error + Send + Sync + 'static>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ConfroidError {
#[error("configuration contains multiple errors:\n{errors}", errors = ErrorList(.errors))]
Multiple {
errors: Vec<ConfroidError>,
},
#[error("environment variable `{var_name}` (field `{field}`) is not set")]
EnvVarNotFound {
var_name: String,
field: String,
},
#[error(
"environment variable `{var_name}` (field `{field}`) has invalid value `{value}`: {parser_error}"
)]
EnvVarInvalid {
var_name: String,
field: String,
value: String,
#[source]
parser_error: ParserError,
},
#[error(
"vector `{field}` has invalid index `{index}` in variable `{var_name}`; expected a number"
)]
VecIndexInvalid {
var_name: String,
field: String,
index: String,
},
#[error(
"vector `{field}` (var `{var_name}`) is missing index {missing}; indices must be contiguous starting at 0"
)]
VecIndexGap {
var_name: String,
field: String,
missing: usize,
},
}
impl ConfroidError {
#[doc(hidden)]
pub fn from_errors(errors: Vec<Self>) -> Self {
let mut flattened = Vec::new();
for error in errors {
flattened.extend(error.into_errors());
}
match flattened.len() {
0 => panic!("ConfroidError::from_errors requires at least one error"),
1 => flattened.pop().expect("length was checked"),
_ => Self::Multiple { errors: flattened },
}
}
pub fn errors(&self) -> &[Self] {
match self {
Self::Multiple { errors } => errors,
error => std::slice::from_ref(error),
}
}
#[doc(hidden)]
pub fn into_errors(self) -> Vec<Self> {
match self {
Self::Multiple { errors } => errors,
error => vec![error],
}
}
}
struct ErrorList<'a>(&'a [ConfroidError]);
impl fmt::Display for ErrorList<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, error) in self.0.iter().enumerate() {
if index > 0 {
formatter.write_str("\n")?;
}
write!(formatter, "- {error}")?;
}
Ok(())
}
}
pub type Result<T> = core::result::Result<T, ConfroidError>;