Skip to main content

cotis_defaults/render_commands/
render_t_list.rs

1//! Typed heterogeneous render-command lists.
2//!
3//! `RenderTList` encodes command streams as nested types instead of homogeneous
4//! vectors. This is mainly used by advanced pipe/renderer integrations.
5//!
6//! # Examples
7//!
8//! ```rust,ignore
9//! use cotis_defaults::render_commands::render_t_list::{Embed, Here, RenderTList, There};
10//!
11//! type List = RenderTList<A, RenderTList<B, ()>>;
12//! type Sub = RenderTList<B, ()>;
13//!
14//! let sub: Sub = RenderTList::Type(B);
15//! let full: List = sub.embed::<List, There<Here>>();
16//! ```
17//Todo: list macro for easy of use
18mod private {
19    pub trait Sealed {}
20    impl Sealed for () {}
21    impl<T, L: Sealed + super::IsRenderList> Sealed for super::RenderTList<T, L> {}
22}
23
24/// Marker trait for valid render-list nodes.
25pub trait IsRenderList: private::Sealed {}
26impl IsRenderList for () {}
27impl<T, L: IsRenderList> IsRenderList for RenderTList<T, L> {}
28
29#[derive(Clone, Debug)]
30#[cfg_attr(
31    feature = "serialization",
32    derive(serde::Serialize, serde::Deserialize)
33)]
34#[cfg_attr(
35    feature = "serialization",
36    serde(bound(deserialize = "
37    T: serde::de::DeserializeOwned,
38    L: serde::de::DeserializeOwned,
39"))
40)]
41pub enum RenderTList<T, L: IsRenderList> {
42    /// A command value at the current list position.
43    Type(T),
44    /// The rest of the list.
45    List(L),
46}
47
48/// Index marker for the current position.
49pub struct Here;
50/// Index marker for one position deeper in the list.
51pub struct There<Idx>(std::marker::PhantomData<Idx>);
52
53/// Embeds one list type into another at a type-level index.
54pub trait Embed<Target, Idx> {
55    /// Converts `self` into `Target`.
56    fn embed(self) -> Target;
57}
58
59// Base case: we've already reached the matching position.
60impl<T, L: IsRenderList> Embed<RenderTList<T, L>, Here> for RenderTList<T, L> {
61    fn embed(self) -> RenderTList<T, L> {
62        self
63    }
64}
65
66// Recursive case: peel one head off Target and recurse into its tail.
67impl<H, TailTarget, Source, Idx> Embed<RenderTList<H, TailTarget>, There<Idx>> for Source
68where
69    TailTarget: IsRenderList,
70    Source: Embed<TailTarget, Idx>,
71{
72    fn embed(self) -> RenderTList<H, TailTarget> {
73        RenderTList::List(self.embed())
74    }
75}
76
77#[cfg(test)]
78mod test {
79    use crate::render_commands::render_t_list::{Embed, Here, RenderTList, There};
80
81    type Full = RenderTList<i32, RenderTList<i32, ()>>;
82
83    #[test]
84    fn embeds_sublist_at_tail_position() {
85        let sub = RenderTList::Type(42);
86        let full = Embed::<Full, There<Here>>::embed(sub);
87
88        match full {
89            RenderTList::List(RenderTList::Type(42)) => {}
90            other => panic!("unexpected embed result: {other:?}"),
91        }
92    }
93}