devela 0.27.0

A development layer of coherence.
Documentation
// devela::data::id::handle
//
//! Defines [`define_handle!`] macro.
//

#[cfg(any(test, feature = "_docs_examples"))]
define_handle! {
    [offset: u8 + crate::NonExtremeU8; ]

    #[doc = crate::_tags!(example uid)]
    /// An example handle generated by [`define_handle!`].
    #[doc = crate::_doc_location!("data")]
    ///
    /// Demonstrates a minimal two-field handle using niche-aware `u8` storage.
    pub HandleExample;
}

#[doc = crate::_tags!(construction uid)]
/// Defines a lightweight handle type.
#[doc = crate::_doc_location!("data/id")]
///
/// A *handle* is a lightweight, copyable semantic reference that identifies
/// an entry within a managed collection, such as an arena, list, or graph.
///
/// Handles are plain data values — they contain only small scalar fields
/// (like offsets, lengths, or indices) and no lifetimes or ownership.
///
/// Handles form the connective tissue of the data layer,
/// bridging raw storage with higher-level structure.
///
/// # Examples
/// A simple handle for an arena.
/// ```
/// # use devela::{define_handle, NonExtremeUsize};
/// define_handle! {
///     [offset: usize+NonExtremeUsize; ]
///     /// A custom handle.
///     pub MyHandle;
/// }
/// ```
/// See also [`HandleExample`].
#[cfg_attr(cargo_primary_package, doc(hidden))]
#[macro_export]
macro_rules! define_handle {
    // point of entry
    (
     [
      offset: $prim:ident+$T:ty;
     ]

     $(#[$handle_attr:meta])*
     $vis:vis $Handle:ident $(;)?
     ) => {
         $crate::define_handle![%handle
             [offset:$prim+$T;]
             $(#[$handle_attr])* $vis $Handle ];
    };
    // calls the necessary arms in order.
    (
     %handle
     [offset:$prim:ident+$T:ty;]
     $(#[$handle_attr:meta])* $vis:vis $Handle:ident) => { $crate::paste! {

        $crate::define_handle![%main
            [offset:$prim+$T;]
            $(#[$handle_attr])* $vis $Handle ];

        // #[cfg(test)]
        // $crate::define_handle![%tests $Handle, [<test_ $Handle>]];
    }};
    (
     %main
     [offset:$prim:ident+$T:ty;]
     $(#[$handle_attr:meta])* $vis:vis $Handle:ident) => {
        $(#[$handle_attr])*
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        $vis struct $Handle {
            offset: $crate::MaybeNiche::<$T>,
            len: $crate::MaybeNiche::<$T>,
        }

        impl $crate::ConstInit for $Handle {
            const INIT: Self = Self::new(<$T>::INIT, <$T>::INIT);
        }

        /// Fundamental const methods for creation and access.
        #[allow(dead_code)]
        impl $Handle {
            /* constructors */

            /// Creates a new handle from an `offset` and `len`.
            #[must_use] #[inline(always)]
            $vis const fn new(offset: $T, len: $T) -> Self {
                let offset = $crate::MaybeNiche::<$T>::new(offset);
                let len = $crate::MaybeNiche::<$T>::new(len);
                Self { offset, len }
            }

            /// Creates a new handle from a primitive `offset` and `len`.
            ///
            /// Returns `None` if any of the values are invalid.
            #[inline(always)]
            $vis const fn from_prim(offset: $prim, len: $prim)
                -> Result<Self, $crate::InvalidValue> {
                let offset = $crate::unwrap![ok? $crate::MaybeNiche::<$T>::try_from_prim(offset)];
                let len = $crate::unwrap![ok? $crate::MaybeNiche::<$T>::try_from_prim(len)];
                Ok(Self { offset, len })
            }

            // MAYBE: if we gate the unsafe with a macro argument
            // /// Creates a new handle from a primitive `offset` and `len`, without any checks.
            // /// # Safety
            // /// Callers must ensure that the values satisfies the validity constraints.
            // #[must_use] #[inline(always)]
            // $vis const fn from_prim_unchecked(offset: $prim, len: $prim) -> Self {
            //     unimplemented![]
            // }

            /// Creates a new handle from a *lossy* primitive `offset` and `len`.
            ///
            /// Converting invalid inputs into a valid but *approximate* representation.
            #[must_use] #[inline(always)]
            $vis const fn from_prim_lossy(offset: $prim, len: $prim) -> Self {
                let offset = $crate::MaybeNiche::<$T>::from_prim_lossy(offset);
                let len = $crate::MaybeNiche::<$T>::from_prim_lossy(len);
                Self { offset, len }
            }

            /// Creates a new handle from a primitive `offset` and `len`.
            ///
            /// Returns `None` if any of the values can't fit in the primitive representation,
            /// or if it's not valid for the current niche.
            #[inline(always)]
            $vis const fn try_from_usize(offset: usize, len: usize)
                -> Result<Self, $crate::NicheValueError> {
                let o = $crate::unwrap![ok? $crate::MaybeNiche::<$T>::try_from_usize(offset)];
                let len = $crate::unwrap![ok? $crate::MaybeNiche::<$T>::try_from_usize(len)];
                Ok(Self { offset: o, len })
            }

            /* accessors */

            /// Returns the length of the stored data.
            #[must_use] #[inline(always)]
            #[allow(clippy::len_without_is_empty)]
            $vis const fn len(self) -> $T { self.len.get() }
            /// Returns the length of the stored data as the corresponding primitive.
            #[must_use] #[inline(always)]
            $vis const fn len_prim(self) -> $prim { self.len.get_prim() }

            /// Returns the length of the stored data as a usize.
            #[inline(always)]
            $vis const fn len_usize(self) -> Result<usize, $crate::Overflow> {
                self.len.try_to_usize()
            }
            /// Returns the length of the stored data as a usize, saturating at the numeric bounds.
            #[must_use] #[inline(always)]
            $vis const fn len_usize_saturating(self) -> usize {
                self.len.to_usize_saturating()
            }

            /// Returns the offset of the stored data.
            #[must_use] #[inline(always)]
            $vis const fn offset(self) -> $T { self.offset.get() }
            /// Returns the offset of the stored data as the corresponding primitive.
            #[must_use] #[inline(always)]
            $vis const fn offset_prim(self) -> $prim { self.offset.get_prim() }

            /// Returns the offset of the stored data as a usize.
            #[inline(always)]
            $vis const fn offset_usize(self) -> Result<usize, $crate::Overflow> {
                self.offset.try_to_usize()
            }
            /// Returns the offset of the stored data as a usize, saturating at the numeric bounds.
            #[must_use] #[inline(always)]
            $vis const fn offset_usize_saturating(self) -> usize {
                self.offset.to_usize_saturating()
            }
        }
    };
}
#[doc(inline)]
pub use define_handle;