use std::{error::Error, fmt, panic};
use crate::{
descriptor::{ConfigValueDescriptor, VarDescriptor},
error::ConfigInitError,
layer::Layer,
};
pub struct ExecResult<'a> {
#[doc(hidden)]
pub config: &'a VarDescriptor,
#[doc(hidden)]
pub error: Option<Box<dyn Error + 'a>>,
}
impl<'a> ExecResult<'a> {
#[doc(hidden)]
pub fn from_config<T>(config: &'a T) -> Self
where
&'a T: Layer,
Box<dyn Error + 'a>: From<<&'a T as Layer>::Error>,
T: ConfigValueDescriptor,
{
Self {
config: config.get_descriptor(),
error: config.try_get().err().map(From::from),
}
}
}
#[derive(Debug)]
pub(crate) struct ExecFailedResult<'a> {
config: &'a VarDescriptor,
error: Box<dyn Error + 'a>,
}
pub struct FmtExecResults<'a> {
pub(crate) correct_vars: Vec<&'a VarDescriptor>,
pub(crate) incorrect_vars: Vec<ExecFailedResult<'a>>,
}
impl fmt::Display for FmtExecResults<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"Got {} incorrect variable{}",
self.incorrect_vars.len(),
if self.incorrect_vars.len() > 1 {
"s"
} else {
""
}
)?;
for var in &self.incorrect_vars {
writeln!(f, "- `{}`: {}", var.config.var_name, var.error)?;
}
writeln!(
f,
"Got {} valid variable{}",
self.correct_vars.len(),
if self.correct_vars.len() > 1 { "s" } else { "" }
)?;
for var in &self.correct_vars {
writeln!(f, "- `{}`", var.var_name)?;
}
writeln!(f, "Note: full required environment description:")?;
for var_desc in self
.incorrect_vars
.iter()
.map(|v| v.config)
.chain(self.correct_vars.iter().copied())
{
writeln!(f, "- {var_desc}")?;
}
Ok(())
}
}
pub fn fmt_exec_results<'a, I>(results: I) -> FmtExecResults<'a>
where
I: IntoIterator<Item = ExecResult<'a>>,
{
let mut incorrect_vars = Vec::new();
let mut correct_vars = Vec::new();
for result in results {
if let Some(err) = result.error {
incorrect_vars.push(ExecFailedResult {
config: result.config,
error: err,
});
} else {
correct_vars.push(result.config);
}
}
FmtExecResults {
correct_vars,
incorrect_vars,
}
}
pub trait ConfigInitializer {
type Iter<'a>: IntoIterator<Item = ExecResult<'a>>
where
Self: 'a;
fn init_raw(&self) -> Self::Iter<'_>;
fn try_init(&self) -> Result<(), ConfigInitError<'_>> {
let res = fmt_exec_results(self.init_raw());
if res.incorrect_vars.is_empty() {
Ok(())
} else {
Err(ConfigInitError { error: res })
}
}
fn init(&self) {
self.try_init().unwrap_or_else(|e| {
panic!("{e}");
});
}
}