congen 0.2.0

congen helps you build configuration systems that support partial updates from structured changes and CLI input
Documentation
use std::{
    any::Any,
    borrow::Cow,
    ffi::OsStr,
    rc::Rc,
    sync::Arc,
};

use crate::{
    Configuration,
    internal::{
        ChangeVerb, CongenChange, CongenDefault, CongenInternal, Description, NotSupported,
        ParseError, VerbError,
    },
};

#[derive(Debug, Clone, Default)]
pub struct BoxChange<C>(pub C);

#[derive(Debug, Clone, Default)]
pub struct ArcChange<C>(pub C);

#[derive(Debug, Clone, Default)]
pub struct RcChange<C>(pub C);

impl<T> Configuration for Box<T>
where
    T: Configuration + 'static,
{
}

impl<T> CongenInternal for Box<T>
where
    T: Configuration + 'static,
{
    type CongenChange = BoxChange<T::CongenChange>;

    fn apply_change_with_inner_default(
        &mut self,
        change: Self::CongenChange,
        inner_default: Option<fn() -> Box<dyn Any>>,
    ) {
        self.as_mut()
            .apply_change_with_inner_default(change.0, inner_default);
    }

    fn description(field_name: &'static str) -> Description {
        T::description(field_name)
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Box::new(T::default()?))
    }

    fn type_name() -> Cow<'static, str> {
        T::type_name()
    }
}

impl<T> CongenDefault for Box<T>
where
    T: CongenDefault + Configuration + 'static,
{
}

impl<C, T> CongenChange for BoxChange<C>
where
    C: CongenChange<Configuration = T>,
    T: Configuration + 'static,
{
    type Configuration = Box<T>;

    fn empty() -> Self {
        Self(C::empty())
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Self(C::default()?))
    }

    fn parse(input: &OsStr) -> Result<Result<Self, ParseError>, NotSupported> {
        C::parse(input).map(|parsed| parsed.map(Self))
    }

    fn apply_change(&mut self, change: Self) {
        self.0.apply_change(change.0);
    }

    fn from_path_and_verb<'a, P>(path: P, verb: ChangeVerb) -> Result<Self, VerbError>
    where
        P: Iterator<Item = &'a str>,
    {
        C::from_path_and_verb(path, verb).map(Self)
    }

    fn unwrap_field(self) -> Result<Self::Configuration, Self> {
        match self.0.unwrap_field() {
            Ok(unwrapped) => Ok(Box::new(unwrapped)),
            Err(change) => Err(Self(change)),
        }
    }
}

impl<T> Configuration for Arc<T>
where
    T: Configuration + 'static,
{
}

impl<T> CongenInternal for Arc<T>
where
    T: Configuration + 'static,
{
    type CongenChange = ArcChange<T::CongenChange>;

    fn apply_change_with_inner_default(
        &mut self,
        change: Self::CongenChange,
        inner_default: Option<fn() -> Box<dyn Any>>,
    ) {
        let this = Arc::get_mut(self).expect(
            "called apply_change_with_inner_default on Arc<T> with multiple strong references",
        );
        this.apply_change_with_inner_default(change.0, inner_default);
    }

    fn description(field_name: &'static str) -> Description {
        T::description(field_name)
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Arc::new(T::default()?))
    }

    fn type_name() -> Cow<'static, str> {
        T::type_name()
    }
}

impl<T> CongenDefault for Arc<T>
where
    T: CongenDefault + Configuration + 'static,
{
}

impl<C, T> CongenChange for ArcChange<C>
where
    C: CongenChange<Configuration = T>,
    T: Configuration + 'static,
{
    type Configuration = Arc<T>;

    fn empty() -> Self {
        Self(C::empty())
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Self(C::default()?))
    }

    fn parse(input: &OsStr) -> Result<Result<Self, ParseError>, NotSupported> {
        C::parse(input).map(|parsed| parsed.map(Self))
    }

    fn apply_change(&mut self, change: Self) {
        self.0.apply_change(change.0);
    }

    fn from_path_and_verb<'a, P>(path: P, verb: ChangeVerb) -> Result<Self, VerbError>
    where
        P: Iterator<Item = &'a str>,
    {
        C::from_path_and_verb(path, verb).map(Self)
    }

    fn unwrap_field(self) -> Result<Self::Configuration, Self> {
        match self.0.unwrap_field() {
            Ok(unwrapped) => Ok(Arc::new(unwrapped)),
            Err(change) => Err(Self(change)),
        }
    }
}

impl<T> Configuration for Rc<T>
where
    T: Configuration + 'static,
{
}

impl<T> CongenInternal for Rc<T>
where
    T: Configuration + 'static,
{
    type CongenChange = RcChange<T::CongenChange>;

    fn apply_change_with_inner_default(
        &mut self,
        change: Self::CongenChange,
        inner_default: Option<fn() -> Box<dyn Any>>,
    ) {
        let this = Rc::get_mut(self)
            .expect("called apply_change_with_inner_default on Rc<T> with multiple references");
        this.apply_change_with_inner_default(change.0, inner_default);
    }

    fn description(field_name: &'static str) -> Description {
        T::description(field_name)
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Rc::new(T::default()?))
    }

    fn type_name() -> Cow<'static, str> {
        T::type_name()
    }
}

impl<T> CongenDefault for Rc<T>
where
    T: CongenDefault + Configuration + 'static,
{
}

impl<C, T> CongenChange for RcChange<C>
where
    C: CongenChange<Configuration = T>,
    T: Configuration + 'static,
{
    type Configuration = Rc<T>;

    fn empty() -> Self {
        Self(C::empty())
    }

    fn default() -> Result<Self, NotSupported> {
        Ok(Self(C::default()?))
    }

    fn parse(input: &OsStr) -> Result<Result<Self, ParseError>, NotSupported> {
        C::parse(input).map(|parsed| parsed.map(Self))
    }

    fn apply_change(&mut self, change: Self) {
        self.0.apply_change(change.0);
    }

    fn from_path_and_verb<'a, P>(path: P, verb: ChangeVerb) -> Result<Self, VerbError>
    where
        P: Iterator<Item = &'a str>,
    {
        C::from_path_and_verb(path, verb).map(Self)
    }

    fn unwrap_field(self) -> Result<Self::Configuration, Self> {
        match self.0.unwrap_field() {
            Ok(unwrapped) => Ok(Rc::new(unwrapped)),
            Err(change) => Err(Self(change)),
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate self as congen;

    use std::{ffi::OsString, rc::Rc, sync::Arc};

    use crate::{
        Configuration,
        internal::{ChangeVerb, CongenChange, CongenInternal},
    };

    #[derive(Configuration, Debug)]
    struct Inner {
        value: u32,
        #[congen(default)]
        maybe: Option<u32>,
    }

    #[derive(Configuration, Debug)]
    struct Wrapped {
        plain: Inner,
        boxed: Box<Inner>,
        arc: Arc<Inner>,
        rc: Rc<Inner>,
        plain_num: u32,
        boxed_num: Box<u32>,
        arc_num: Arc<u32>,
        rc_num: Rc<u32>,
    }

    fn base_config() -> Wrapped {
        Wrapped {
            plain: Inner {
                value: 1,
                maybe: Some(10),
            },
            boxed: Box::new(Inner {
                value: 2,
                maybe: Some(10),
            }),
            arc: Arc::new(Inner {
                value: 3,
                maybe: Some(10),
            }),
            rc: Rc::new(Inner {
                value: 4,
                maybe: Some(10),
            }),
            plain_num: 5,
            boxed_num: Box::new(6),
            arc_num: Arc::new(7),
            rc_num: Rc::new(8),
        }
    }

    fn apply_change(config: &mut Wrapped, path: &[&str], verb: ChangeVerb) {
        type Change = <Wrapped as CongenInternal>::CongenChange;
        let change = <Change as CongenChange>::from_path_and_verb(path.iter().copied(), verb)
            .expect("create change");
        config.apply_change(change);
    }

    #[test]
    fn wrappers_support_nested_changes_like_inner_type() {
        let mut config = base_config();

        apply_change(
            &mut config,
            &["plain", "value"],
            ChangeVerb::Set(OsString::from("11")),
        );
        apply_change(
            &mut config,
            &["boxed", "value"],
            ChangeVerb::Set(OsString::from("12")),
        );
        apply_change(
            &mut config,
            &["arc", "value"],
            ChangeVerb::Set(OsString::from("13")),
        );
        apply_change(
            &mut config,
            &["rc", "value"],
            ChangeVerb::Set(OsString::from("14")),
        );

        assert_eq!(config.plain.value, 11);
        assert_eq!(config.boxed.value, 12);
        assert_eq!(config.arc.value, 13);
        assert_eq!(config.rc.value, 14);
    }

    #[test]
    fn wrappers_support_use_default_like_inner_type() {
        let mut config = base_config();

        apply_change(&mut config, &["plain", "maybe"], ChangeVerb::UseDefault);
        apply_change(&mut config, &["boxed", "maybe"], ChangeVerb::UseDefault);
        apply_change(&mut config, &["arc", "maybe"], ChangeVerb::UseDefault);
        apply_change(&mut config, &["rc", "maybe"], ChangeVerb::UseDefault);

        assert!(config.plain.maybe.is_none());
        assert!(config.boxed.maybe.is_none());
        assert!(config.arc.maybe.is_none());
        assert!(config.rc.maybe.is_none());
    }

    #[test]
    fn wrappers_support_field_set_like_inner_type() {
        let mut config = base_config();

        apply_change(
            &mut config,
            &["plain_num"],
            ChangeVerb::Set(OsString::from("15")),
        );
        apply_change(
            &mut config,
            &["boxed_num"],
            ChangeVerb::Set(OsString::from("16")),
        );
        apply_change(
            &mut config,
            &["arc_num"],
            ChangeVerb::Set(OsString::from("17")),
        );
        apply_change(
            &mut config,
            &["rc_num"],
            ChangeVerb::Set(OsString::from("18")),
        );

        assert_eq!(config.plain_num, 15);
        assert_eq!(*config.boxed_num, 16);
        assert_eq!(*config.arc_num, 17);
        assert_eq!(*config.rc_num, 18);
    }
}