hirola_core/
render.rs

1//! Trait for describing how components and other custom types should be rendered into DOM nodes.
2use crate::{
3    generic_node::GenericNode,
4    templating::flow::{Indexed, IndexedProps},
5};
6use futures_signals::{
7    signal::{Mutable, ReadOnlyMutable, SignalExt},
8    signal_vec::{Filter, MutableSignalVec, MutableVec, SignalVec, SignalVecExt},
9};
10use std::{
11    fmt::{Debug, Display},
12    iter::Enumerate,
13    pin::Pin,
14};
15
16#[derive(Debug)]
17pub enum Error {
18    DomError(Box<dyn Debug>),
19}
20
21/// Trait for describing how something should be rendered into nodes.
22pub trait Render<N: GenericNode> {
23    /// Called during the initial render when creating the nodes inside a dom.
24    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error>;
25}
26
27/// Does nothing
28impl<N: GenericNode> Render<N> for () {
29    fn render_into(self: Box<Self>, _dom: &N) -> Result<(), Error> {
30        Ok(())
31    }
32}
33
34impl<N: GenericNode> Render<N> for &str {
35    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
36        let child = &N::text_node(*self);
37        parent.append_child(child);
38        Ok(())
39    }
40}
41
42impl<N: GenericNode> Render<N> for String {
43    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
44        let child = &N::text_node(&self);
45        parent.append_child(child);
46        Ok(())
47    }
48}
49
50impl<N: GenericNode> Render<N> for &String {
51    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
52        let child = &N::text_node(&self);
53        parent.append_child(child);
54        Ok(())
55    }
56}
57
58/// Renders `A`, then `B`
59impl<A: Render<N>, B: Render<N>, N: GenericNode> Render<N> for (A, B) {
60    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
61        Box::new(self.0).render_into(parent)?;
62        Box::new(self.1).render_into(parent)
63    }
64}
65
66/// Renders `A`, then `B`, then `C`
67impl<A: Render<N>, B: Render<N>, C: Render<N>, N: GenericNode> Render<N> for (A, B, C) {
68    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
69        Box::new(self.0).render_into(parent)?;
70        Box::new(self.1).render_into(parent)?;
71        Box::new(self.2).render_into(parent)
72    }
73}
74
75/// Renders `T` or nothing
76impl<T: Render<N>, N: GenericNode> Render<N> for Option<T> {
77    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
78        match *self {
79            None => Ok(()),
80            Some(x) => Box::new(x).render_into(parent),
81        }
82    }
83}
84
85impl<T: Render<N>, N: GenericNode> Render<N> for Vec<T> {
86    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
87        for elem in *self {
88            Box::new(elem).render_into(parent)?;
89        }
90        Ok(())
91    }
92}
93
94impl<T: Render<N>, N: GenericNode> Render<N> for Box<T> {
95    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
96        (*self).render_into(parent)?;
97        Ok(())
98    }
99}
100
101impl<T: Display + Clone + 'static, N: GenericNode> Render<N> for Mutable<T> {
102    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
103        let child = N::text_node(&self.get_cloned().to_string());
104        parent.append_child(&child);
105        let fut = self.signal_ref(move |e| child.update_inner_text(&e.to_string()));
106        parent.effect(fut.to_future());
107        Ok(())
108    }
109}
110
111pub struct MappedVec<T, G: GenericNode> {
112    pub iter: Pin<Box<dyn SignalVec<Item = T>>>,
113    callback: Box<dyn Fn(T) -> G>,
114}
115
116// pub struct Mapped<T, G: GenericNode> {
117//     pub signal: Pin<Box<dyn Signal<Item = T>>>,
118//     callback: Box<dyn Fn(T) -> G>,
119// }
120
121pub trait MapRender<G: GenericNode> {
122    type Item;
123    type Output;
124    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> Self::Output;
125}
126
127impl<T: Clone + 'static, G: GenericNode> MapRender<G> for MutableSignalVec<T> {
128    type Item = T;
129    type Output = MappedVec<Self::Item, G>;
130    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> MappedVec<Self::Item, G> {
131        MappedVec {
132            iter: Box::pin(self),
133            callback: Box::new(callback),
134        }
135    }
136}
137
138// impl<T: Clone + 'static, G: GenericNode> MapRender<G> for Mutable<T> {
139//     type Item = T;
140//     type Output = Mapped<Self::Item, G>;
141
142//     fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> Mapped<Self::Item, G> {
143//         Mapped {
144//             signal: Box::pin(self.signal_cloned()),
145//             callback: Box::new(callback),
146//         }
147//     }
148// }
149
150impl<
151        T: Clone + 'static,
152        I: SignalVec<Item = T> + 'static,
153        F: FnMut(&T) -> bool + 'static,
154        G: GenericNode,
155    > MapRender<G> for Filter<I, F>
156{
157    type Item = T;
158    type Output = MappedVec<Self::Item, G>;
159
160    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> MappedVec<Self::Item, G> {
161        MappedVec {
162            iter: Box::pin(self),
163            callback: Box::new(callback),
164        }
165    }
166}
167
168impl<T: Clone + 'static, I: Iterator<Item = T>, G: GenericNode> MapRender<G> for Enumerate<I> {
169    type Item = (usize, T);
170    type Output = MappedVec<Self::Item, G>;
171
172    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> MappedVec<Self::Item, G> {
173        let items = self.collect();
174        MappedVec {
175            iter: Box::pin(MutableVec::new_with_values(items).signal_vec_cloned()),
176            callback: Box::new(callback),
177        }
178    }
179}
180
181impl<T: Clone + 'static + PartialEq, I: SignalVec<Item = T> + Unpin + 'static, G: GenericNode>
182    MapRender<G> for futures_signals::signal_vec::Enumerate<I>
183{
184    type Item = (ReadOnlyMutable<Option<usize>>, T);
185    type Output = MappedVec<Self::Item, G>;
186
187    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> MappedVec<Self::Item, G> {
188        MappedVec {
189            iter: Box::pin(self.to_signal_cloned().to_signal_vec()),
190            callback: Box::new(callback),
191        }
192    }
193}
194
195impl<T: Clone + 'static, G: GenericNode> MapRender<G> for Vec<T> {
196    type Item = T;
197    type Output = MappedVec<Self::Item, G>;
198
199    fn map_render(self, callback: impl Fn(Self::Item) -> G + 'static) -> MappedVec<Self::Item, G> {
200        MappedVec {
201            iter: Box::pin(MutableVec::new_with_values(self).signal_vec_cloned()),
202            callback: Box::new(callback),
203        }
204    }
205}
206
207impl<T: 'static + Clone, N: GenericNode> Render<N> for MappedVec<T, N> {
208    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
209        let template = {
210            #[allow(clippy::type_complexity)]
211            let props: IndexedProps<
212                T,
213                Pin<Box<dyn SignalVec<Item = T>>>,
214                Box<dyn Fn(T) -> N>,
215                N,
216            > = IndexedProps {
217                iterable: self.iter,
218                template: self.callback,
219            };
220
221            Indexed { props }
222        };
223        Box::new(template).render_into(parent)?;
224        Ok(())
225    }
226}
227
228/// Renders `O` or `E`
229impl<O: Render<N>, E: Render<N>, N: GenericNode> Render<N> for std::result::Result<O, E> {
230    fn render_into(self: Box<Self>, parent: &N) -> Result<(), Error> {
231        match *self {
232            Ok(o) => Box::new(o).render_into(parent),
233            Err(e) => Box::new(e).render_into(parent),
234        }
235    }
236}