on c5e1dfd help sign in
dodrio/src/render.rs
153 lines 4.2 kB history raw
1use crate::Node;
2use bumpalo::Bump;
3use std::any::Any;
4use std::rc::Rc;
5use wasm_bindgen::UnwrapThrowExt;
7/// A trait for any component that can be rendered to HTML.
8///
9/// Takes a shared reference to `self` and generates the virtual DOM that
10/// represents its rendered HTML.
11///
12/// ## `Bump` Allocation
13///
14/// `Render` implementations can use the provided `Bump` for very fast
15/// allocation for anything that needs to be allocated during rendering.
16///
17/// ## The `'a: 'bump` Lifetime Bound
18///
19/// The `'a: 'bump` bounds enforce that `self` outlives the given bump
20/// allocator. This means that if `self` contains a string, the string does not
21/// need to be copied into the output `Node` and can be used by reference
22/// instead (i.e. it prevents accidentally using the string after its been
23/// freed). The `'a: 'bump` bound also enables abstractions like
24/// `dodrio::Cached` that can re-use cached `Node`s across `render`s without
25/// copying them.
26///
27/// ## Example
28///
29/// ```no_run
30/// use dodrio::{bumpalo::Bump, Node, Render};
31///
32/// pub struct MyComponent;
33///
34/// impl Render for MyComponent {
35/// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
36/// where
37/// 'a: 'bump
38/// {
39/// use dodrio::builder::*;
40///
41/// p(bump)
42/// .children([
43/// text("This is "),
44/// strong(bump).children([text("my component")]).finish(),
45/// text(" rendered!"),
46/// ])
47/// .finish()
48/// }
49/// }
50/// ```
51pub trait Render {
52 /// Render `self` as a virtual DOM. Use the given `Bump` for temporary
53 /// allocations.
54 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
55 where
56 'a: 'bump;
59impl<'r, R> Render for &'r R
60where
61 R: Render,
63 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
64 where
65 'a: 'bump,
66 {
67 (**self).render(bump)
68 }
71impl<R> Render for Rc<R>
72where
73 R: Render,
75 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
76 where
77 'a: 'bump,
78 {
79 (**self).render(bump)
80 }
83/// A `RootRender` is a render component that can be the root rendering component
84/// mounted to a virtual DOM.
85///
86/// In addition to rendering, it must also be `'static` so that it can be owned
87/// by the virtual DOM and `Any` so that it can be downcast to its concrete type
88/// by event listener callbacks.
89///
90/// You do not need to implement this trait by hand: there is a blanket
91/// implementation for all `Render` types that fulfill the `RootRender`
92/// requirements.
93pub trait RootRender: Any + Render {
94 /// Get this `&RootRender` trait object as an `&Any` trait object reference.
95 fn as_any(&self) -> &Any;
97 /// Get this `&mut RootRender` trait object as an `&mut Any` trait object
98 /// reference.
99 fn as_any_mut(&mut self) -> &mut Any;
102impl<T: Any + Render> RootRender for T {
103 fn as_any(&self) -> &Any {
104 self
105 }
107 fn as_any_mut(&mut self) -> &mut Any {
108 self
109 }
112impl dyn RootRender {
113 /// Downcast this shared `&dyn RootRender` trait object reference to its
114 /// underlying concrete type.
115 ///
116 /// # Panics
117 ///
118 /// Panics if this virtual DOM's root rendering component is not an `R`
119 /// instance.
120 pub fn unwrap_ref<R: RootRender>(&self) -> &R {
121 self.as_any()
122 .downcast_ref::<R>()
123 .expect_throw("bad `RootRender::unwrap_ref` call")
124 }
126 /// Downcast this exclusive `&mut dyn RootRender` trait object reference to
127 /// its underlying concrete type.
128 ///
129 /// # Panics
130 ///
131 /// Panics if this virtual DOM's root rendering component is not an `R`
132 /// instance.
133 pub fn unwrap_mut<R: RootRender>(&mut self) -> &mut R {
134 self.as_any_mut()
135 .downcast_mut::<R>()
136 .expect_throw("bad `RootRender::unwrap_ref` call")
137 }
140#[cfg(test)]
141mod tests {
142 #[test]
143 fn render_is_object_safe() {
144 #[allow(dead_code)]
145 fn takes_dyn_render(_: &dyn super::Render) {}
146 }
148 #[test]
149 fn root_render_is_object_safe() {
150 #[allow(dead_code)]
151 fn takes_dyn_render(_: &dyn super::RootRender) {}
152 }