compose_rt/
scope.rs

1use std::fmt::{self, Debug, Formatter};
2use std::hash::Hash;
3use std::marker::PhantomData;
4use std::ops::DerefMut;
5
6use generational_box::GenerationalBox;
7use slab::Slab;
8
9use crate::composer::NodeKey;
10use crate::{AnyData, ComposeNode, Composer, Loc, Node, State, StateId};
11
12pub struct Scope<S, N>
13where
14    N: ComposeNode,
15{
16    pub id: ScopeId,
17    pub(crate) composer: GenerationalBox<Composer<N>>,
18    ty: PhantomData<S>,
19}
20
21impl<S, N> Clone for Scope<S, N>
22where
23    N: ComposeNode,
24{
25    fn clone(&self) -> Self {
26        *self
27    }
28}
29
30impl<S, N> Copy for Scope<S, N> where N: ComposeNode {}
31
32impl<S, N> Scope<S, N>
33where
34    S: 'static,
35    N: ComposeNode,
36{
37    #[inline(always)]
38    pub(crate) fn new(id: ScopeId, composer: GenerationalBox<Composer<N>>) -> Self {
39        Self {
40            id,
41            composer,
42            ty: PhantomData,
43        }
44    }
45
46    #[inline(always)]
47    pub(crate) fn set_key(&mut self, key: usize) {
48        self.id.set_key(key);
49    }
50
51    #[track_caller]
52    #[inline(always)]
53    pub fn child<C>(&self) -> Scope<C, N>
54    where
55        C: 'static,
56    {
57        let id = ScopeId::new();
58        Scope::new(id, self.composer)
59    }
60
61    #[track_caller]
62    pub fn use_state<F, T>(&self, init: F) -> State<T, N>
63    where
64        T: 'static,
65        F: Fn() -> T + 'static,
66    {
67        let mut c = self.composer.write();
68        let c = c.deref_mut();
69        let current_node_key = c.current_node_key;
70        let id = StateId::new(current_node_key);
71        let scope_states = c.states.entry(current_node_key).or_default();
72        let _ = scope_states.entry(id).or_insert_with(|| Box::new(init()));
73        State::new(id, self.composer)
74    }
75
76    #[track_caller]
77    #[inline(always)]
78    pub fn key<C>(&self, key: usize, content: C)
79    where
80        C: Fn(Self) + 'static,
81    {
82        self.composer.write().key_stack.push(key);
83        content(*self);
84        self.composer.write().key_stack.pop();
85    }
86
87    pub fn create_node<C, T, I, A, F, U>(
88        &self,
89        child_scope: Scope<T, N>,
90        content: C,
91        input: I,
92        factory: F,
93        update: U,
94    ) where
95        T: 'static,
96        C: Fn(Scope<T, N>) + Clone + 'static,
97        I: Fn() -> A + Clone + 'static,
98        A: 'static,
99        F: Fn(A, &mut N::Context) -> N + Clone + 'static,
100        U: Fn(&mut N, A, &mut N::Context) + Clone + 'static,
101    {
102        let parent_scope = *self;
103        let composable = move || {
104            let mut current_scope = child_scope;
105            let (parent_node_key, current_node_key, is_dirty) = {
106                let mut c = parent_scope.composer.write();
107                if let Some(key) = c.key_stack.last().copied() {
108                    current_scope.set_key(key);
109                }
110                let parent_node_key = c.current_node_key;
111                c.start_node(parent_node_key, current_scope.id);
112                let current_node_key = c.current_node_key;
113                let is_visited = c.composables.contains_key(&current_node_key);
114                let is_dirty = c.dirty_nodes.contains(&current_node_key);
115                if !is_dirty && is_visited {
116                    c.skip_node(parent_node_key);
117                    return current_node_key;
118                }
119                drop(c);
120                let args = input();
121                let mut c = parent_scope.composer.write();
122                let c = c.deref_mut();
123                update_node(
124                    current_node_key,
125                    &mut c.context,
126                    &mut c.nodes,
127                    args,
128                    &factory,
129                    &update,
130                );
131                (parent_node_key, current_node_key, is_dirty)
132            };
133            content(current_scope);
134            let mut c = parent_scope.composer.write();
135            let c = c.deref_mut();
136            if is_dirty {
137                c.dirty_nodes.remove(&current_node_key);
138            }
139            c.end_node(parent_node_key);
140            current_node_key
141        };
142        let current_node_key = composable();
143        let mut c = parent_scope.composer.write();
144        c.composables
145            .entry(current_node_key)
146            .or_insert_with(|| Box::new(composable));
147    }
148
149    #[inline(always)]
150    pub fn create_any_node<C, T, I, A, E, F, U>(
151        &self,
152        child_scope: Scope<T, N>,
153        content: C,
154        input: I,
155        factory: F,
156        update: U,
157    ) where
158        T: 'static,
159        C: Fn(Scope<T, N>) + Clone + 'static,
160        I: Fn() -> A + Clone + 'static,
161        A: 'static,
162        N: AnyData<E>,
163        E: 'static,
164        F: Fn(A, &mut N::Context) -> E + Clone + 'static,
165        U: Fn(&mut E, A, &mut N::Context) + Clone + 'static,
166    {
167        self.create_node(
168            child_scope,
169            content,
170            input,
171            move |args, ctx| {
172                let e = factory(args, ctx);
173                AnyData::new(e)
174            },
175            move |n, args, ctx| {
176                let e = n.value_mut();
177                update(e, args, ctx);
178            },
179        );
180    }
181}
182
183// workaround of borrowing both context and nodes from Composer
184// https://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
185#[inline(always)]
186fn update_node<N, A, F, U>(
187    node_key: NodeKey,
188    context: &mut N::Context,
189    nodes: &mut Slab<Node<N>>,
190    args: A,
191    factory: &F,
192    update: &U,
193) where
194    N: ComposeNode,
195    A: 'static,
196    F: Fn(A, &mut N::Context) -> N + Clone + 'static,
197    U: Fn(&mut N, A, &mut N::Context) + Clone + 'static,
198{
199    let node = nodes.get_mut(node_key).unwrap();
200    if let Some(data) = node.data.as_mut() {
201        update(data, args, context);
202    } else {
203        let data = factory(args, context);
204        node.data = Some(data);
205    }
206}
207
208#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
209pub struct ScopeId {
210    pub loc: Loc,
211    pub key: usize,
212}
213
214impl ScopeId {
215    #[track_caller]
216    #[inline(always)]
217    pub fn new() -> Self {
218        let loc = Loc::new();
219        Self { loc, key: 0 }
220    }
221
222    #[inline(always)]
223    pub fn set_key(&mut self, key: usize) {
224        self.key = key;
225    }
226
227    #[inline(always)]
228    pub fn get_key(&self) -> usize {
229        self.key
230    }
231}
232
233impl Debug for ScopeId {
234    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
235        write!(f, "{:?}|{}", self.loc, self.key)
236    }
237}
238
239#[derive(Debug, Clone, Copy)]
240pub struct Root;