Skip to main content

maud_extensions/
slot.rs

1// Runtime slot wrapper types used by generated component builders.
2use core::marker::PhantomData;
3
4use maud::{Markup, PreEscaped, Render};
5
6/// Typed slot storage for component fields.
7///
8/// The type parameter expresses the semantic child contract, while the runtime
9/// value stores already-rendered markup.
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct Slot<T> {
12    markup: String,
13    marker: PhantomData<fn() -> T>,
14}
15
16impl<T> Default for Slot<T> {
17    fn default() -> Self {
18        Self {
19            markup: String::new(),
20            marker: PhantomData,
21        }
22    }
23}
24
25impl<T> Slot<T> {
26    /// Returns true when the slot currently stores no rendered content.
27    pub fn is_empty(&self) -> bool {
28        self.markup.is_empty()
29    }
30
31    /// Converts the slot back into Maud markup.
32    pub fn into_markup(self) -> Markup {
33        PreEscaped(self.markup)
34    }
35}
36
37impl<T> Render for Slot<T> {
38    fn render(&self) -> Markup {
39        PreEscaped(self.markup.clone())
40    }
41}
42
43impl From<Markup> for Slot<Markup> {
44    fn from(value: Markup) -> Self {
45        Self {
46            markup: value.into_string(),
47            marker: PhantomData,
48        }
49    }
50}
51
52impl From<Vec<Markup>> for Slot<Vec<Markup>> {
53    fn from(value: Vec<Markup>) -> Self {
54        let mut slot = Self::default();
55        for item in value {
56            slot.push(item);
57        }
58        slot
59    }
60}
61
62impl<T> Slot<Vec<T>> {
63    /// Appends one rendered item to this repeated slot.
64    pub fn push(&mut self, value: Markup) {
65        self.markup.push_str(&value.into_string());
66    }
67}
68
69/// Convenience alias for repeated markup-backed slot storage.
70pub type Slots = Slot<Vec<Markup>>;