feattle-core 3.0.0

Featture toggles for Rust, extensible and with background synchronization and administration UI
Documentation
/// Define an `enum` that can be used as a type for a feattle
///
/// The generated `enum` will have these standard traits: `Debug`, `Clone`, `Copy`, `Eq`,
/// `PartialEq`, `PartialOrd`, `Ord`, `FromStr`, `Display`. And mainly, it will implement
/// [`crate::FeattleStringValue`] so that it can be used a feattle type.
///
/// Only `enum`s whose variants do not carry any extra information are supported.
///
/// # Examples
/// In the simplest form:
/// ```
/// use feattle_core::feattle_enum;
///
/// feattle_enum! {
///     enum Colors { Red, Green, Blue }
/// }
/// ```
///
/// However, it also works with other visibility keywords and additional attributes on the enum
/// itself or its variants. Those attributes will not be modified by this lib, allowing composition
/// with other libs. For example, you can also make the enum `Serialize`:
/// ```
/// use feattle_core::feattle_enum;
/// use serde::Serialize;
///
/// feattle_enum! {
///     #[derive(Serialize)]
///     pub(crate) enum Colors {
///         #[serde(rename = "R")]
///         Red,
///         #[serde(rename = "G")]
///         Green,
///         #[serde(rename = "B")]
///         Blue,
///     }
/// }
/// ```
#[macro_export]
macro_rules! feattle_enum {
    (
        $(#[$enum_meta:meta])*
        $visibility:vis enum $name:ident {
            $(
                $(#[$variant_meta:meta])*
                $variant:ident
            ),+ $(,)?
        }
    ) => {
        #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
        $(#[$enum_meta])*
        $visibility enum $name {
            $(
                $(#[$variant_meta])*
                $variant
            ),+
        }

        impl ::std::str::FromStr for $name {
            type Err = $crate::__internal::ParseError;
            fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
                match s {
                    $(
                        stringify!($variant) => ::std::result::Result::Ok(Self::$variant)
                    ),+,
                    _ => ::std::result::Result::Err($crate::__internal::ParseError)
                }
            }
        }

        impl ::std::fmt::Display for $name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                let as_str = match self {
                    $(
                        Self::$variant => stringify!($variant)
                    ),+
                };
                ::std::write!(f, "{}", as_str)
            }
        }

        impl $name {
            const VARIANTS: &'static [&'static str] = &[
                $(
                    stringify!($variant)
                ),+
            ];
        }

        impl $crate::FeattleStringValue for $name {
            fn serialized_string_format() -> $crate::StringFormat {
                let variants = Self::VARIANTS.join(", ");
                $crate::StringFormat {
                    kind: $crate::StringFormatKind::Choices(&Self::VARIANTS),
                    tag: format!("enum {{{}}}", variants),
                }
            }
        }
    }
}

#[macro_export]
#[doc(hidden)]
macro_rules! __init_field {
    ($default:expr) => {
        $default
    };
    () => {
        Default::default()
    };
}

/// The main macro of this crate, used to generate a struct that will provide the Feattles
/// functionalities.
///
/// See more at the [crate level](crate).
#[macro_export]
macro_rules! feattles {
    (
    $(#[$meta:meta])*
    $visibility:vis struct $name:ident {
        $(
            $(#[doc=$description:tt])*
            $key:ident: $type:ty $(= $default:expr)?
        ),*
        $(,)?
    }
) => {
        use $crate::__internal;

        $(#[$meta])*
        #[derive(Debug)]
        $visibility struct $name(__internal::FeattlesImpl<__Feattles>);

        impl __internal::FeattlesPrivate for $name {
            type FeattleStruct = __Feattles;

            fn _read(
                &self,
            ) -> __internal::RwLockReadGuard<'_, __internal::InnerFeattles<Self::FeattleStruct>>
            {
                self.0.inner_feattles.read()
            }

            fn _write(
                &self,
            ) -> __internal::RwLockWriteGuard<'_, __internal::InnerFeattles<Self::FeattleStruct>>
            {
                self.0.inner_feattles.write()
            }
        }

        impl __internal::Feattles for $name {
            fn new(persistence: __internal::Arc<dyn __internal::Persist>) -> Self {
                $name(__internal::FeattlesImpl::new(
                    persistence,
                    __Feattles {
                        $(
                            $key: __internal::Feattle::new(
                                stringify!($key),
                                concat!($($description),*).trim(),
                                $crate::__init_field!($($default)?),
                            )
                        ),*
                    },
                ))
            }

            fn persistence(&self) -> &__internal::Arc<dyn __internal::Persist> {
                &self.0.persistence
            }

            fn keys(&self) -> &'static [&'static str] {
                &[$(stringify!($key)),*]
            }

            fn definition(&self, key: &str) -> Option<__internal::FeattleDefinition> {
                use __internal::FeattlesPrivate;
                let inner = self._read();
                match key {
                    $(stringify!($key) => Some(inner.feattles_struct.$key.definition()),)*
                    _ => None,
                }
            }
        }

        impl $name {
            $(
                pub fn $key(&self) -> __internal::MappedRwLockReadGuard<$type> {
                    __internal::RwLockReadGuard::map(self.0.inner_feattles.read(), |inner| {
                        inner.feattles_struct.$key.value()
                    })
                }
            )*
        }

        #[derive(Debug)]
        pub struct __Feattles {
            $($key: __internal::Feattle<$type>),*
        }

        impl __internal::FeattlesStruct for __Feattles {
            fn try_update(
                &mut self,
                key: &str,
                value: Option<__internal::CurrentValue>,
            ) -> Result<Option<__internal::CurrentValue>, __internal::FromJsonError> {
                match key {
                    $(stringify!($key) => self.$key.try_update(value),)*
                    _ => unreachable!(),
                }
            }
        }
    }
}