dynconfig 0.1.2

Dynamically change fields of a struct based on a path.
Documentation
use crate::{Error, Result};

/// An updatable value.
pub trait Value: Sized {
    /// Parse `value` and create a new item.
    fn new(value: &str) -> Result<Self>;

    /// Parse `value` and update `self`.
    fn update(&mut self, value: &str) -> Result<()> {
        *self = Self::new(value)?;
        Ok(())
    }
}

///////////////////////////////////////////////////////////////////////////////

macro_rules! impl_value_by_parse {
    ($t:ty) => {
        impl Value for $t {
            fn new(value: &str) -> Result<Self> {
                value.parse().map_err(Error::custom)
            }
        }
    };
    ($($t:ty,)*) => {
        $(impl_value_by_parse! { $t })*
    };
}

mod core_impl {
    use super::*;

    impl_value_by_parse! {
        bool, char,
        f32, f64,
        i8, i16, i32, i64, isize,
        u8, u16, u32, u64, usize,

        core::net::IpAddr, core::net::Ipv4Addr, core::net::Ipv6Addr,
        core::net::SocketAddr, core::net::SocketAddrV4, core::net::SocketAddrV6,

        core::num::NonZero<i8>, core::num::NonZero<i16>,
        core::num::NonZero<i32>, core::num::NonZero<i64>,
        core::num::NonZero<i128>, core::num::NonZero<isize>,

        core::num::NonZero<u8>, core::num::NonZero<u16>,
        core::num::NonZero<u32>, core::num::NonZero<u64>,
        core::num::NonZero<u128>, core::num::NonZero<usize>,
    }

    impl<T> Value for Option<T>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            T::new(value).map(Some)
        }
    }
}

mod alloc_impl {
    use super::*;

    use alloc::string::String;

    impl Value for String {
        fn new(value: &str) -> Result<Self> {
            Ok(String::from(value))
        }
    }
}

#[cfg(feature = "std")]
mod std_impl {
    use super::*;

    use std::ffi::OsString;
    use std::path::PathBuf;

    impl Value for OsString {
        fn new(value: &str) -> Result<Self> {
            Ok(OsString::from(value))
        }
    }

    impl Value for PathBuf {
        fn new(value: &str) -> Result<Self> {
            Ok(PathBuf::from(value))
        }
    }
}

#[cfg(feature = "module")]
mod module_impl {
    use super::*;

    use module::types::*;

    impl<T> Value for First<T>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            T::new(value).map(First)
        }
    }

    impl<T> Value for Last<T>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            T::new(value).map(Last)
        }
    }

    impl Value for Lines {
        fn new(value: &str) -> Result<Self> {
            Ok(Lines::new(value))
        }
    }

    impl<T> Value for NoMerge<T>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            T::new(value).map(Self)
        }
    }

    impl<T> Value for Ordered<T>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            let err = match T::new(value) {
                Ok(x) => return Ok(Ordered::new(x)),
                Err(e) => e,
            };

            if let Some(value) = value.strip_prefix(':') {
                T::new(value).map(|x| Ordered::with_order(x, ordered::Order::After))
            } else if let Some(value) = value.strip_suffix(':') {
                T::new(value).map(|x| Ordered::with_order(x, ordered::Order::Before))
            } else {
                Err(err)
            }
        }
    }

    impl<T, const DEFAULT: isize> Value for Overridable<T, DEFAULT>
    where
        T: Value,
    {
        fn new(value: &str) -> Result<Self> {
            let err = match T::new(value) {
                Ok(x) => return Ok(Overridable::new(x)),
                Err(e) => e,
            };

            let Some((priority, value)) = value.split_once(':') else {
                return Err(err);
            };

            let priority = priority
                .trim()
                .parse::<isize>()
                .map(overridable::Priority::from)
                .map_err(|e| Error::custom(format_args!("invalid priority: {e}")))?;

            let value = T::new(value)?;

            Ok(Overridable::with_priority(value, priority))
        }
    }
}