dodrio/
render_context.rs

1use crate::{
2    cached::{Cached, TemplateId},
3    cached_set::{CacheId, CachedSet},
4    Node, Render,
5};
6use bumpalo::Bump;
7use fxhash::FxHashMap;
8use std::fmt;
9
10/// Common context available to all `Render` implementations.
11///
12/// Notably, the `RenderContext` gives access to the bump arena that the virtual
13/// DOM should be allocated within. This is available via the `bump` field.
14pub struct RenderContext<'a> {
15    /// The underlying bump arena that virtual DOMs are rendered into.
16    ///
17    /// ## Example
18    ///
19    /// ```
20    /// use dodrio::RenderContext;
21    ///
22    /// // Given a rendering context, allocate an i32 inside its bump arena.
23    /// fn foo<'a>(cx: &mut RenderContext<'a>) -> &'a mut i32 {
24    ///     cx.bump.alloc(42)
25    /// }
26    /// ```
27    pub bump: &'a Bump,
28
29    pub(crate) cached_set: &'a crate::RefCell<CachedSet>,
30
31    pub(crate) templates: &'a mut FxHashMap<TemplateId, Option<CacheId>>,
32
33    // Prevent exhaustive matching on the rendering context, so we can always
34    // add more members in a semver-compatible way.
35    _non_exhaustive: (),
36}
37
38impl fmt::Debug for RenderContext<'_> {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        f.debug_struct("RenderContext")
41            .field("bump", &self.bump)
42            .finish()
43    }
44}
45
46impl<'a> RenderContext<'a> {
47    pub_unstable_internal! {
48        pub(crate) fn new(
49            bump: &'a Bump,
50            cached_set: &'a crate::RefCell<CachedSet>,
51            templates: &'a mut FxHashMap<TemplateId, Option<CacheId>>
52        ) -> Self {
53            RenderContext {
54                bump,
55                cached_set,
56                templates,
57                _non_exhaustive: (),
58            }
59        }
60    }
61
62    pub(crate) fn cache<F>(&mut self, pinned: bool, template: Option<CacheId>, f: F) -> CacheId
63    where
64        F: for<'b> FnOnce(&mut RenderContext<'b>) -> Node<'b>,
65    {
66        CachedSet::insert(self, pinned, template, f)
67    }
68
69    /// Get or create the cached template for `Cached<R>`.
70    pub(crate) fn template<R>(&mut self) -> Option<CacheId>
71    where
72        R: 'static + Default + for<'b> Render<'b>,
73    {
74        let template_id = Cached::<R>::template_id();
75        if let Some(cache_id) = self.templates.get(&template_id).cloned() {
76            return cache_id;
77        }
78
79        // Prevent re-entrancy from infinite looping. Any attempts to get `R`'s
80        // template while constructing the template will simply fail to use the
81        // templated fast path.
82        self.templates.insert(template_id, None);
83
84        // Render the default `R` and save that as the template for all
85        // `Cached<R>`s.
86        let cache_id = self.cache(true, None, |nested_cx| R::default().render(nested_cx));
87        self.templates.insert(template_id, Some(cache_id));
88        Some(cache_id)
89    }
90}
91
92impl<'a, 'b> From<&'b RenderContext<'a>> for &'a Bump {
93    #[inline]
94    fn from(cx: &'b RenderContext<'a>) -> &'a Bump {
95        cx.bump
96    }
97}
98
99impl<'a, 'b, 'c> From<&'c &'b mut RenderContext<'a>> for &'a Bump {
100    #[inline]
101    fn from(cx: &'c &'b mut RenderContext<'a>) -> &'a Bump {
102        cx.bump
103    }
104}