1use super::{JsValue, Scope};
4use crate::{ContextKey, OwnerHandle};
5use std::cell::RefCell;
6
7struct Action {
8 owner: OwnerHandle,
9 setup: Box<dyn FnOnce() -> Result<(), JsValue>>,
10}
11
12#[derive(Default)]
13pub(super) struct CommitQueue(RefCell<Vec<Action>>);
14
15impl CommitQueue {
16 pub(super) fn is_idle(&self) -> bool {
18 self.0.borrow().is_empty()
19 }
20}
21
22struct MountContext;
23impl ContextKey for MountContext {
24 type Value = CommitQueue;
25}
26
27impl Scope {
28 pub(super) fn prepare_queue(&mut self, parent: Option<&OwnerHandle>) {
29 self.mount_parent = parent.cloned();
30 if self.owner.is_disposed() {
31 self.mount_queue = None;
32 return;
33 }
34 let queue = parent
35 .and_then(|parent| parent.context::<MountContext>())
36 .unwrap_or_else(|| {
37 let owner = self.owner();
38 owner
39 .provide::<MountContext>(CommitQueue::default())
40 .expect("fresh component owner");
41 owner.context::<MountContext>().expect("just provided")
42 });
43 self.mount_queue = Some(queue);
44 }
45
46 #[doc(hidden)]
50 pub fn before_commit(
51 &mut self,
52 setup: impl FnOnce() -> Result<(), JsValue> + 'static,
53 ) -> Result<(), JsValue> {
54 if self.owner.is_disposed() {
55 return Err(JsValue::from_str("cannot initialize a disposed component"));
56 }
57 if self.owner.is_active() {
58 return setup();
59 }
60 let queue = self.mount_queue.as_ref().expect("prepared component queue");
61 queue.0.borrow_mut().push(Action {
62 owner: self.owner(),
63 setup: Box::new(setup),
64 });
65 Ok(())
66 }
67
68 #[doc(hidden)]
71 pub fn finish_prepare(&self) -> Result<(), JsValue> {
72 if self.owner.is_disposed() {
73 return Err(JsValue::from_str("cannot commit a disposed component"));
74 }
75 self.owner.mark_mount_ready();
76 if self
77 .mount_parent
78 .as_ref()
79 .is_some_and(|parent| !parent.is_active())
80 {
81 return Ok(());
82 }
83 self.finish_prepare_subtree()
84 }
85
86 pub fn finish_prepare_subtree(&self) -> Result<(), JsValue> {
90 if self.owner.is_disposed() {
91 return Err(JsValue::from_str("cannot prepare a disposed subtree"));
92 }
93 self.owner.mark_mount_ready();
94 if let Some(queue) = &self.mount_queue {
95 let pending = queue.0.take();
96 let owner = self.owner();
97 let (ready, waiting): (Vec<_>, Vec<_>) = pending
98 .into_iter()
99 .filter(|action| !action.owner.is_disposed())
100 .partition(|action| {
101 action.owner.is_within(&owner) && action.owner.is_mount_ready()
102 });
103 queue.0.borrow_mut().extend(waiting);
104 for action in ready {
105 if !action.owner.is_disposed() {
106 (action.setup)()?;
107 }
108 }
109 }
110 if self.owner.is_disposed() {
111 return Err(JsValue::from_str(
112 "component disposed during initialization",
113 ));
114 }
115 Ok(())
116 }
117
118 pub fn try_commit(&self) -> Result<(), JsValue> {
121 if self.owner.is_active()
125 && self
126 .mount_queue
127 .as_ref()
128 .is_none_or(|queue| queue.0.borrow().is_empty())
129 {
130 return Ok(());
131 }
132 self.finish_prepare()?;
133 self.owner.commit();
134 if self.owner.is_disposed() {
135 return Err(JsValue::from_str("component disposed during activation"));
136 }
137 Ok(())
138 }
139}