//! Helper macros.

/// Helper to define field access functions for types.
///
/// Helps to cut down boilerplate, especially for types with many fields.
#[macro_export]
macro_rules! field_access {
    // Field access for fields with the same getter and setter type.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: $field_ty:ty $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub const fn $field(&self) -> $field_ty {
                        self.$field
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $field_ty) {
                        self.$field = val;
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $field_ty) -> Self {
                        Self {
                            $field: val,
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for fields with different getter and setter types.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: as_ref { $field_ty:ty } $(,)?
        )+
    }) => {
        field_access! {
            $ty {
                $(
                    $(#[$field_doc])*
                    $field: as_ref { &$field_ty, $field_ty },
                )+
            }
        }
    };

    // Field access for fields with different getter and setter types.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: as_ref { $get_ty:ty, $set_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> $get_ty {
                        &self.$field
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $set_ty) {
                        self.$field = val;
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $set_ty) -> Self {
                        Self {
                            $field: val,
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for fields with `Cow<'_, T>` setter types.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: cow { $cow_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> &$cow_ty {
                        &self.$field
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(&mut self, val: C) {
                        self.$field = C::into(val).into();
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(self, val: C) -> Self {
                        Self {
                            $field: C::into(val).into(),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for fields with `Zeroizing<Cow<'_, T>>` setter types.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: zeroize_cow { $cow_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> &$cow_ty {
                        &self.$field
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(&mut self, val: C) {
                        self.$field = ::zeroize::Zeroizing::new(C::into(val).into());
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(self, val: C) -> Self {
                        Self {
                            $field: ::zeroize::Zeroizing::new(C::into(val).into()),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for `Option`-wrapped types.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option { $field_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub const fn $field(&self) -> Option<$field_ty> {
                        self.$field
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $field_ty) {
                        self.$field = Some(val);
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$field_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $field_ty) -> Self {
                        Self {
                            $field: Some(val),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option::as_deref` for the getter.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_deref { $get_ty:ty, $set_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<$get_ty> {
                        self.$field.as_deref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $set_ty) {
                        self.$field = Some(val);
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$set_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $set_ty) -> Self {
                        Self {
                            $field: Some(val),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option::as_ref` for the getter.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_ref { $get_ty:ty, $set_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<$get_ty> {
                        self.$field.as_ref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $set_ty) {
                        self.$field = Some(val);
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$set_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $set_ty) -> Self {
                        Self {
                            $field: Some(val),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option::as_ref` for the getter.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_ref { $field_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<&$field_ty> {
                        self.$field.as_ref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>](&mut self, val: $field_ty) {
                        self.$field = Some(val);
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$field_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>](self, val: $field_ty) -> Self {
                        Self {
                            $field: Some(val),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option<&$cow_ty>` for the getter,
    // and generic `Cow<'_, $cow_ty>` for the setters.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_cow_ref { $cow_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<&$cow_ty> {
                        self.$field.as_ref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(&mut self, val: C) {
                        self.$field = Some(C::into(val).into());
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$cow_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(self, val: C) -> Self {
                        Self {
                            $field: Some(C::into(val).into()),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option<&$cow_ty>` for the getter,
    // and generic `Cow<'_, $cow_ty>` for the setters.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_cow_ref { $cow_ty:ty, $own_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<&$cow_ty> {
                        self.$field.as_ref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(&mut self, val: C) {
                        self.$field = Some(C::into(val).into());
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$own_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(self, val: C) -> Self {
                        Self {
                            $field: Some(C::into(val).into()),
                            ..self
                        }
                    }
                )+
            }
        }
    };

    // Field access for types that return `Option<&$cow_ty>` for the getter,
    // and generic `Cow<'_, $cow_ty>` for the setters.
    ($ty:ident {
        $(
            $(#[$field_doc:meta])*
            $field:ident: option_cow_deref { $cow_ty:ty, $own_ty:ty } $(,)?
        )+
    }) => {
        ::paste::paste! {
            impl $ty {
                $(
                    #[doc = "Gets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn $field(&self) -> Option<&$cow_ty> {
                        self.$field.as_deref()
                    }

                    #[doc = "Sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<set_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(&mut self, val: C) {
                        self.$field = Some(C::into(val).into());
                    }

                    #[doc = "Unsets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    pub fn [<unset_ $field>](&mut self) -> Option<$own_ty> {
                        self.$field.take()
                    }

                    #[doc = "Builder function that sets the [" $ty "] `" $field "`."]
                    $(
                        #[doc = ""]
                        #[$field_doc]
                    )*
                    #[allow(clippy::needless_update)]
                    pub fn [<with_ $field>]<'a, C: Into<std::borrow::Cow<'a, $cow_ty>>>(self, val: C) -> Self {
                        Self {
                            $field: Some(C::into(val).into()),
                            ..self
                        }
                    }
                )+
            }
        }
    };
}
