confcade 0.0.1

Minimalistic cascading configuration
Documentation
use std::borrow::Cow;

pub use confcade_macros::Confcade;

pub trait Confcade {
    const INITIAL: Self;
    type Concrete;

    fn fallback(&mut self, other: Self);
    fn finalize(self) -> Result<Self::Concrete, CascadeError>;
}

impl<T> Confcade for Option<T> {
    const INITIAL: Self = None;

    type Concrete = T;

    fn fallback(&mut self, other: Self) {
        if self.is_none() {
            *self = other;
        }
    }

    fn finalize(self) -> Result<Self::Concrete, CascadeError> {
        self.ok_or_else(CascadeError::default)
    }
}

#[derive(thiserror::Error, Debug, Default)]
#[error("Missing value for {missing_fields:?}")]
pub struct CascadeError {
    pub missing_fields: Vec<Cow<'static, str>>,
}

impl CascadeError {
    pub fn __maybe_append_from(&mut self, field_name: &'static str, err: Option<Self>) {
        let Some(err) = err else {
            return;
        };
        if err.missing_fields.is_empty() {
            self.missing_fields.push(Cow::from(field_name));
        } else {
            self.missing_fields.extend(err.missing_fields.into_iter().map(|sub_field_name| {
                Cow::from(format!("{field_name}.{sub_field_name}"))
            }));
        }
    }
}