cotis-defaults 0.1.0-alpha.1

Modular Rust UI framework core
Documentation
//! Typed heterogeneous render-command lists.
//!
//! `RenderTList` encodes command streams as nested types instead of homogeneous
//! vectors. This is mainly used by advanced pipe/renderer integrations.
//!
//! # Examples
//!
//! ```rust,ignore
//! use cotis_defaults::render_commands::render_t_list::{Embed, Here, RenderTList, There};
//!
//! type List = RenderTList<A, RenderTList<B, ()>>;
//! type Sub = RenderTList<B, ()>;
//!
//! let sub: Sub = RenderTList::Type(B);
//! let full: List = sub.embed::<List, There<Here>>();
//! ```
//Todo: list macro for easy of use
mod private {
    pub trait Sealed {}
    impl Sealed for () {}
    impl<T, L: Sealed + super::IsRenderList> Sealed for super::RenderTList<T, L> {}
}

/// Marker trait for valid render-list nodes.
pub trait IsRenderList: private::Sealed {}
impl IsRenderList for () {}
impl<T, L: IsRenderList> IsRenderList for RenderTList<T, L> {}

#[derive(Clone, Debug)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
    feature = "serialization",
    serde(bound(deserialize = "
    T: serde::de::DeserializeOwned,
    L: serde::de::DeserializeOwned,
"))
)]
pub enum RenderTList<T, L: IsRenderList> {
    /// A command value at the current list position.
    Type(T),
    /// The rest of the list.
    List(L),
}

/// Index marker for the current position.
pub struct Here;
/// Index marker for one position deeper in the list.
pub struct There<Idx>(std::marker::PhantomData<Idx>);

/// Embeds one list type into another at a type-level index.
pub trait Embed<Target, Idx> {
    /// Converts `self` into `Target`.
    fn embed(self) -> Target;
}

// Base case: we've already reached the matching position.
impl<T, L: IsRenderList> Embed<RenderTList<T, L>, Here> for RenderTList<T, L> {
    fn embed(self) -> RenderTList<T, L> {
        self
    }
}

// Recursive case: peel one head off Target and recurse into its tail.
impl<H, TailTarget, Source, Idx> Embed<RenderTList<H, TailTarget>, There<Idx>> for Source
where
    TailTarget: IsRenderList,
    Source: Embed<TailTarget, Idx>,
{
    fn embed(self) -> RenderTList<H, TailTarget> {
        RenderTList::List(self.embed())
    }
}

#[cfg(test)]
mod test {
    use crate::render_commands::render_t_list::{Embed, Here, RenderTList, There};

    type Full = RenderTList<i32, RenderTList<i32, ()>>;

    #[test]
    fn embeds_sublist_at_tail_position() {
        let sub = RenderTList::Type(42);
        let full = Embed::<Full, There<Here>>::embed(sub);

        match full {
            RenderTList::List(RenderTList::Type(42)) => {}
            other => panic!("unexpected embed result: {other:?}"),
        }
    }
}