hirola_ssr/
lib.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::rc::{Rc, Weak};
4use std::{fmt, mem};
5
6use hirola_core::generic_node::GenericNode;
7use hirola_core::render::{Error, Render};
8
9/// Rendering backend for Server Side Rendering, aka. SSR.
10/// Offers interior mutability and is not thread safe.
11#[derive(Debug)]
12enum SsrNodeType {
13    Element(RefCell<Element>),
14    Comment(RefCell<Comment>),
15    Text(RefCell<Text>),
16    Fragment(RefCell<Fragment>),
17}
18
19#[derive(Debug, Clone)]
20struct SsrNodeInner {
21    ty: Rc<SsrNodeType>,
22    /// No parent if `Weak::upgrade` returns `None`.
23    parent: RefCell<Weak<SsrNodeInner>>,
24}
25
26#[derive(Debug, Clone)]
27pub struct SsrNode(Rc<SsrNodeInner>);
28
29impl PartialEq for SsrNode {
30    fn eq(&self, other: &Self) -> bool {
31        Rc::ptr_eq(&self.0.ty, &other.0.ty)
32    }
33}
34
35impl Eq for SsrNode {}
36
37impl SsrNode {
38    fn new(ty: SsrNodeType) -> Self {
39        Self(Rc::new(SsrNodeInner {
40            ty: Rc::new(ty),
41            parent: RefCell::new(Weak::new()), // no parent
42        }))
43    }
44
45    fn set_parent(&self, parent: Weak<SsrNodeInner>) {
46        if let Some(old_parent) = self.parent_node() {
47            old_parent.try_remove_child(self);
48        }
49
50        *self.0.parent.borrow_mut() = parent;
51    }
52
53    #[track_caller]
54    fn unwrap_element(&self) -> &RefCell<Element> {
55        match self.0.ty.as_ref() {
56            SsrNodeType::Element(e) => e,
57            _ => panic!("node is not an element"),
58        }
59    }
60
61    #[track_caller]
62    fn unwrap_text(&self) -> &RefCell<Text> {
63        match &self.0.ty.as_ref() {
64            SsrNodeType::Text(e) => e,
65            _ => panic!("node is not a text node"),
66        }
67    }
68
69    // FIXME: recursively visit Fragments and call try_remove_child
70    fn try_remove_child(&self, child: &Self) {
71        let mut children = match self.0.ty.as_ref() {
72            SsrNodeType::Element(e) => mem::take(&mut e.borrow_mut().children.0),
73            SsrNodeType::Fragment(f) => mem::take(&mut f.borrow_mut().0),
74            _ => panic!("node type cannot have children"),
75        };
76
77        if let Some(index) = children
78            .iter()
79            .enumerate()
80            .find_map(|(i, c)| (c == child).then_some(i))
81        {
82            children.remove(index);
83        } else {
84            // try remove from child Fragments
85            for c in &children {
86                if let SsrNodeType::Fragment(fragment) = c.0.ty.as_ref() {
87                    for c in &fragment.borrow().0 {
88                        c.try_remove_child(child);
89                    }
90                }
91            }
92        }
93
94        match self.0.ty.as_ref() {
95            SsrNodeType::Element(e) => e.borrow_mut().children.0 = children,
96            SsrNodeType::Fragment(f) => f.borrow_mut().0 = children,
97            _ => panic!("node type cannot have children"),
98        };
99    }
100}
101
102impl GenericNode for SsrNode {
103    fn element(tag: &str) -> Self {
104        SsrNode::new(SsrNodeType::Element(RefCell::new(Element {
105            name: tag.to_string(),
106            attributes: HashMap::new(),
107            children: Default::default(),
108        })))
109    }
110
111    fn text_node(text: &str) -> Self {
112        SsrNode::new(SsrNodeType::Text(RefCell::new(Text(text.to_string()))))
113    }
114
115    fn fragment() -> Self {
116        SsrNode::new(SsrNodeType::Fragment(Default::default()))
117    }
118
119    fn marker() -> Self {
120        SsrNode::new(SsrNodeType::Comment(Default::default()))
121    }
122
123    fn set_attribute(&self, name: &str, value: &str) {
124        self.unwrap_element()
125            .borrow_mut()
126            .attributes
127            .insert(name.to_string(), value.to_string());
128    }
129
130    fn append_child(&self, child: &Self) {
131        child.set_parent(Rc::downgrade(&self.0));
132
133        match self.0.ty.as_ref() {
134            SsrNodeType::Element(element) => element.borrow_mut().children.0.push(child.clone()),
135            SsrNodeType::Fragment(fragment) => fragment.borrow_mut().0.push(child.clone()),
136            _ => panic!("node type cannot have children"),
137        }
138    }
139
140    fn insert_child_before(&self, new_node: &Self, reference_node: Option<&Self>) {
141        if let Some(reference_node) = reference_node {
142            debug_assert_eq!(
143                reference_node.parent_node().as_ref(),
144                Some(self),
145                "reference node is not a child of this node"
146            );
147        }
148
149        new_node.set_parent(Rc::downgrade(&self.0));
150
151        let mut children = match self.0.ty.as_ref() {
152            SsrNodeType::Element(e) => mem::take(&mut e.borrow_mut().children.0),
153            SsrNodeType::Fragment(f) => mem::take(&mut f.borrow_mut().0),
154            _ => panic!("node type cannot have children"),
155        };
156
157        match reference_node {
158            None => self.append_child(new_node),
159            Some(reference) => {
160                children.insert(
161                    children
162                        .iter()
163                        .enumerate()
164                        .find_map(|(i, child)| (child == reference).then_some(i))
165                        .expect("couldn't find reference node"),
166                    new_node.clone(),
167                );
168            }
169        }
170
171        match self.0.ty.as_ref() {
172            SsrNodeType::Element(e) => e.borrow_mut().children.0 = children,
173            SsrNodeType::Fragment(f) => f.borrow_mut().0 = children,
174            _ => panic!("node type cannot have children"),
175        };
176    }
177
178    fn remove_child(&self, child: &Self) {
179        let mut children = match self.0.ty.as_ref() {
180            SsrNodeType::Element(e) => mem::take(&mut e.borrow_mut().children.0),
181            SsrNodeType::Fragment(f) => mem::take(&mut f.borrow_mut().0),
182            _ => panic!("node type cannot have children"),
183        };
184
185        let index = children
186            .iter()
187            .enumerate()
188            .find_map(|(i, c)| (c == child).then_some(i))
189            .expect("couldn't find child");
190        children.remove(index);
191
192        match self.0.ty.as_ref() {
193            SsrNodeType::Element(e) => e.borrow_mut().children.0 = children,
194            SsrNodeType::Fragment(f) => f.borrow_mut().0 = children,
195            _ => panic!("node type cannot have children"),
196        };
197    }
198
199    fn replace_child(&self, old: &Self, new: &Self) {
200        new.set_parent(Rc::downgrade(&self.0));
201
202        let mut ele = self.unwrap_element().borrow_mut();
203        let children = &mut ele.children.0;
204        let index = children
205            .iter()
206            .enumerate()
207            .find_map(|(i, c)| (c == old).then_some(i))
208            .expect("Couldn't find child");
209        children[index] = new.clone();
210    }
211
212    fn insert_sibling_before(&self, child: &Self) {
213        child.set_parent(Rc::downgrade(
214            &self.parent_node().expect("no parent for this node").0,
215        ));
216
217        self.parent_node()
218            .unwrap()
219            .insert_child_before(child, Some(self));
220    }
221
222    fn parent_node(&self) -> Option<Self> {
223        self.0.parent.borrow().upgrade().map(SsrNode)
224    }
225
226    fn next_sibling(&self) -> Option<Self> {
227        unimplemented!()
228    }
229
230    fn remove_self(&self) {
231        unimplemented!()
232    }
233
234    fn children(&self) -> RefCell<Vec<SsrNode>> {
235        unimplemented!()
236    }
237
238    fn update_inner_text(&self, text: &str) {
239        self.unwrap_text().borrow_mut().0 = text.to_string();
240    }
241
242    fn replace_children_with(&self, _node: &Self) {
243        unimplemented!()
244    }
245    fn effect(&self, _future: impl std::future::Future<Output = ()> + 'static) {
246        // panic!("SsrNode does not support effects, please use SsrNodeAsync!")
247    }
248}
249
250impl fmt::Display for SsrNode {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        match self.0.ty.as_ref() {
253            SsrNodeType::Element(x) => write!(f, "{}", x.borrow()),
254            SsrNodeType::Comment(x) => write!(f, "{}", x.borrow()),
255            SsrNodeType::Text(x) => write!(f, "{}", x.borrow()),
256            SsrNodeType::Fragment(x) => write!(f, "{}", x.borrow()),
257        }
258    }
259}
260
261impl Render<SsrNode> for SsrNode {
262    fn render_into(self: Box<Self>, parent: &SsrNode) -> Result<(), Error> {
263        parent.append_child(&self);
264        Ok(())
265    }
266}
267
268#[derive(Debug, Clone, Eq, PartialEq)]
269pub struct Element {
270    name: String,
271    attributes: HashMap<String, String>,
272    children: Fragment,
273}
274
275impl fmt::Display for Element {
276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277        write!(f, "<{}", self.name)?;
278        for (name, value) in &self.attributes {
279            write!(
280                f,
281                r#" {}="{}""#,
282                name,
283                html_escape::encode_double_quoted_attribute(value)
284            )?;
285        }
286        write!(f, ">{}</{}>", self.children, self.name)?;
287        Ok(())
288    }
289}
290
291#[derive(Debug, Clone, Eq, PartialEq, Default)]
292pub struct Comment(String);
293
294impl fmt::Display for Comment {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        write!(f, "<!--{}-->", self.0.replace("-->", "--&gt;"))
297    }
298}
299
300#[derive(Debug, Clone, Eq, PartialEq, Default)]
301pub struct Text(String);
302
303impl fmt::Display for Text {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        write!(f, "{}", html_escape::encode_text_minimal(&self.0))
306    }
307}
308
309#[derive(Debug, Clone, Eq, PartialEq, Default)]
310pub struct Fragment(Vec<SsrNode>);
311
312impl fmt::Display for Fragment {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        for child in &self.0 {
315            write!(f, "{}", child)?;
316        }
317        Ok(())
318    }
319}
320
321/// Render a [`SsrNode`] into a static [`String`]. Useful for rendering to a string on the server side.
322pub fn render_to_string(dom: SsrNode) -> Result<String, Error> {
323    let root = SsrNode::fragment();
324    Render::render_into(Box::new(dom), &root)?;
325    Ok(format!("{}", root))
326}
327
328#[cfg(test)]
329mod tests {
330    use super::*;
331    use hirola::prelude::*;
332
333    #[test]
334    fn hello_world() {
335        let node = html! { <p>"Hello World!"</p> };
336
337        assert_eq!(render_to_string(node).unwrap(), "<p>Hello World!</p>");
338    }
339
340    #[test]
341    fn reactive_text() {
342        let count = Mutable::new(0);
343
344        assert_eq!(
345            render_to_string(html! {
346                <p>{count.clone()}</p>
347            })
348            .unwrap(),
349            "<p>0</p>"
350        );
351
352        count.set(1);
353        assert_eq!(
354            render_to_string(html! {
355                <p>{count}</p>
356            })
357            .unwrap(),
358            "<p>1</p>"
359        );
360    }
361
362    #[test]
363    #[should_panic]
364    fn check_reject_effects() {
365        let count = MutableVec::new_with_values(vec![1, 2, 3]);
366
367        let node = html! {
368                <ul>
369                    {count
370                        .signal_vec()
371                        .map_render(move |item| {
372                            html! { <li>{item.to_string()}</li> }
373                        })}
374                </ul>
375            };
376
377        let dom = render_to_string(node).unwrap();
378        assert_eq!("<ul><li>1</li><li>2</li><li>3</li><!----></ul>", dom);
379    }
380}