mls-spec 2.0.0

This crate is a repository of MLS / RFC9420-related data structures.
Documentation
macro_rules! impl_spec_enum {
    (
        $typename:ident($storage:ty);
        serde_repr $serde_repr:literal;
        reserved_priv $reserved_priv_range:expr => $range_error:expr;
        default_range $spec_default_range:expr;
        $(
            $(#[cfg($attr:meta)])*
            $identifier:ident = $value:expr
        ),+
    ) => {
        #[derive(
            Clone,
            Copy,
            PartialEq,
            Eq,
            Hash,
            PartialOrd,
            Ord,
            tls_codec::TlsSerialize,
            tls_codec::TlsDeserialize,
            tls_codec::TlsSize
        )]
        #[cfg_attr(
            feature = "serde",
            derive(serde::Serialize, serde::Deserialize)
        )]
        #[cfg_attr(feature = "serde", serde(try_from = $serde_repr))]
        #[repr(transparent)]
        pub struct $typename($storage);

        impl $typename {
            $(
                $(#[cfg($attr)])*
                pub const $identifier: $storage = $value as $storage;
            )+
            pub(crate) const RESERVED_PRIVATE_USE_RANGE: std::ops::RangeInclusive<$storage> = $reserved_priv_range;
            pub(crate) const SPEC_DEFAULT_RANGE: Option<std::ops::RangeInclusive<$storage>> = $spec_default_range;
        }

        impl $typename {
            #[must_use]
            pub fn all_without_spec_default() -> Vec<Self> {
                let all = [
                    $(
                        $(#[cfg($attr)])*
                        Self($value),
                    )+
                ];

                all.into_iter()
                    .filter(|elem| !elem.is_spec_default())
                    .collect()
            }

            #[must_use]
            pub const fn new_unchecked(value: $storage) -> Self {
                Self(value)
            }

            pub fn new_private_use(value: $storage) -> crate::MlsSpecResult<Self> {
                if !Self::RESERVED_PRIVATE_USE_RANGE.contains(&value) {
                    return Err($range_error);
                }

                Ok(Self(value))
            }

            pub const fn is_spec_default(&self) -> bool {
                let Some(default_range) = &Self::SPEC_DEFAULT_RANGE else {
                    return false;
                };
                *default_range.start() <= self.0 && self.0 <= *default_range.end()
            }

            pub fn is_grease_value(&self) -> bool {
                if Self::RESERVED_PRIVATE_USE_RANGE.contains(&self.0) {
                    return false;
                }

                #[allow(irrefutable_let_patterns)]
                let Ok(self_as_u16): Result<u16, _> = self.0.try_into() else {
                    return false;
                };

                GREASE_VALUES.contains(&self_as_u16)
            }
        }

        impl std::fmt::Display for $typename {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}", self.0)
            }
        }

        impl std::fmt::Debug for $typename {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                let mut const_name = match self {
                    $(
                        $(#[cfg($attr)])*
                        &Self(Self::$identifier) => Some(stringify!($identifier)),
                    )+
                    _ => None
                };

                let typename = stringify!($typename);

                if let Some(const_name) = const_name.take() {
                    write!(f, "{typename}::{const_name}")?;
                } else {
                    write!(f, "{typename}::UNKNOWN[{:4X}]", self.0)?;
                }

                Ok(())
            }
        }

        impl std::ops::Deref for $typename {
            type Target = $storage;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl TryFrom<$storage> for $typename {
            type Error = crate::MlsSpecError;
            fn try_from(value: $storage) -> Result<Self, Self::Error> {
                match value {
                    $(
                        $(#[cfg($attr)])*
                        Self::$identifier => Ok(Self(value)),
                    )+
                    v if Self::RESERVED_PRIVATE_USE_RANGE.contains(&v) => Ok(Self(value)),
                    v if GREASE_VALUES.contains(&v) => Ok(Self(value)),
                    _ => Err(crate::MlsSpecError::InvalidSpecValue)
                }
            }
        }
    };
}

pub(crate) use impl_spec_enum;

macro_rules! ref_forward_tls_impl {
    ($target:ident) => {
        impl tls_codec::Size for &$target {
            fn tls_serialized_len(&self) -> usize {
                (*self).tls_serialized_len()
            }
        }

        impl tls_codec::Serialize for &$target {
            fn tls_serialize<W: std::io::Write>(
                &self,
                writer: &mut W,
            ) -> Result<usize, tls_codec::Error> {
                (*self).tls_serialize(writer)
            }
        }
    };
}

pub(crate) use ref_forward_tls_impl;