orbit/component/
lifecycle.rs

1//! Component lifecycle management for Orbit UI framework
2
3use std::sync::{Arc, Mutex};
4
5use crate::component::{
6    ComponentError, ComponentInstance, Context, LifecyclePhase, UnmountContext, UnmountReason,
7};
8
9/// Manages the lifecycle of components
10pub struct LifecycleManager {
11    /// Current phase of the component
12    phase: LifecyclePhase,
13
14    /// Component instance being managed
15    component: Arc<Mutex<ComponentInstance>>,
16
17    /// Context for the component
18    context: Context,
19}
20
21impl LifecycleManager {
22    /// Create a new lifecycle manager for a component
23    pub fn new(component: ComponentInstance, context: Context) -> Self {
24        Self {
25            phase: LifecyclePhase::Created,
26            component: Arc::new(Mutex::new(component)),
27            context: context.clone(),
28        }
29    }
30
31    /// Get the current lifecycle phase
32    pub fn current_phase(&self) -> LifecyclePhase {
33        self.phase
34    }
35
36    /// Get a reference to the component instance
37    pub fn get_component(&self) -> &Arc<Mutex<ComponentInstance>> {
38        &self.component
39    }
40    /// Initialize the component (post-creation)
41    pub fn initialize(&mut self) -> Result<(), ComponentError> {
42        if self.phase != LifecyclePhase::Created {
43            return Err(ComponentError::InvalidLifecycleTransition(
44                self.phase,
45                "initialize".to_string(),
46            ));
47        }
48        let result = if let Ok(component_instance) = self.component.lock() {
49            if let Ok(mut inner_component) = component_instance.instance.lock() {
50                // Call the component's initialize method through AnyComponent
51                inner_component.any_initialize()
52            } else {
53                Err(ComponentError::LockError(
54                    "Failed to lock inner component for initialization".to_string(),
55                ))
56            }
57        } else {
58            Err(ComponentError::LockError(
59                "Failed to lock component instance for initialization".to_string(),
60            ))
61        };
62
63        if result.is_ok() {
64            // Update context phase
65            self.context.set_lifecycle_phase(LifecyclePhase::Created);
66        }
67
68        result
69    }
70    /// Mount the component to the tree
71    pub fn mount(&mut self) -> Result<(), ComponentError> {
72        if self.phase != LifecyclePhase::Created {
73            return Err(ComponentError::InvalidLifecycleTransition(
74                self.phase,
75                "mount".to_string(),
76            ));
77        }
78
79        // Create mount context
80        let mount_context = crate::component::MountContext::new(
81            self.component
82                .lock()
83                .map_err(|_| {
84                    ComponentError::LockError(
85                        "Failed to lock component for mount context".to_string(),
86                    )
87                })?
88                .instance
89                .lock()
90                .map_err(|_| {
91                    ComponentError::LockError(
92                        "Failed to lock inner component for mount context".to_string(),
93                    )
94                })?
95                .component_id(),
96        );
97
98        // Before mount phase - call before_mount hook
99        let before_mount_result = {
100            let component_instance = self.component.lock().map_err(|_| {
101                ComponentError::LockError("Failed to lock component for before_mount".to_string())
102            })?;
103
104            let mut inner_component = component_instance.instance.lock().map_err(|_| {
105                ComponentError::LockError(
106                    "Failed to lock inner component for before_mount".to_string(),
107                )
108            })?;
109
110            inner_component.any_before_mount()
111        };
112
113        before_mount_result?;
114
115        // Set mounting phase
116        self.phase = LifecyclePhase::Mounting;
117        self.context.set_lifecycle_phase(LifecyclePhase::Mounting);
118
119        // Execute enhanced mount with context
120        let result = {
121            let component_instance = self.component.lock().map_err(|_| {
122                ComponentError::LockError("Failed to lock component for mount".to_string())
123            })?;
124
125            // Get the inner component and call enhanced mount
126            let mut inner_component = component_instance.instance.lock().map_err(|_| {
127                ComponentError::LockError("Failed to lock inner component for mount".to_string())
128            })?;
129
130            // Execute mount hooks first
131            self.context
132                .execute_lifecycle_hooks(LifecyclePhase::Mounting, &mut **inner_component);
133
134            // Call the enhanced mount method with context
135            inner_component.any_on_mount(&mount_context)?;
136
137            // Call the basic mount method through AnyComponent for backward compatibility
138            inner_component.any_mount()
139        };
140
141        // Set mounted phase after successful mount
142        if result.is_ok() {
143            self.phase = LifecyclePhase::Mounted;
144            self.context.set_lifecycle_phase(LifecyclePhase::Mounted);
145
146            // Call after_mount hook
147            let after_mount_result = {
148                let component_instance = self.component.lock().map_err(|_| {
149                    ComponentError::LockError(
150                        "Failed to lock component for after_mount".to_string(),
151                    )
152                })?;
153
154                let mut inner_component = component_instance.instance.lock().map_err(|_| {
155                    ComponentError::LockError(
156                        "Failed to lock inner component for after_mount".to_string(),
157                    )
158                })?;
159
160                inner_component.any_after_mount()
161            };
162
163            if let Err(e) = after_mount_result {
164                // If after_mount fails, we still consider the component mounted but log the error
165                eprintln!("Warning: after_mount failed for component: {e}");
166            }
167        }
168
169        if result.is_err() {
170            // Reset phase on error
171            self.phase = LifecyclePhase::Created;
172            self.context.set_lifecycle_phase(LifecyclePhase::Created);
173        }
174
175        result
176    }
177
178    /// Update the component with new props
179    pub fn update(
180        &mut self,
181        props: Box<dyn crate::component::Props>,
182    ) -> Result<(), ComponentError> {
183        if self.phase != LifecyclePhase::Mounted {
184            return Err(ComponentError::InvalidLifecycleTransition(
185                self.phase,
186                "update".to_string(),
187            ));
188        }
189
190        // Before update phase
191        self.phase = LifecyclePhase::BeforeUpdate;
192        self.context
193            .set_lifecycle_phase(LifecyclePhase::BeforeUpdate);
194
195        // Execute before update hooks and call component's before_update
196        let result = {
197            let mut component = self.component.lock().map_err(|_| {
198                ComponentError::LockError("Failed to lock component for update".to_string())
199            })?;
200
201            // Execute lifecycle hooks before update
202            {
203                let mut instance = component.instance.lock().map_err(|_| {
204                    ComponentError::LockError(
205                        "Failed to lock component instance for update".to_string(),
206                    )
207                })?;
208
209                self.context
210                    .execute_lifecycle_hooks(LifecyclePhase::BeforeUpdate, &mut **instance);
211
212                // Call the component's before_update method with cloned props
213                let props_for_before_update = props.box_clone();
214                instance.any_before_update(props_for_before_update)?;
215            }
216
217            // Update the component with new props
218            {
219                let mut instance = component.instance.lock().map_err(|_| {
220                    ComponentError::LockError(
221                        "Failed to lock component instance for update".to_string(),
222                    )
223                })?;
224
225                // Clone the props using the Props trait's box_clone method
226                let props_for_update = props.box_clone();
227                // Call the component's update method
228                instance.any_update(props_for_update)?;
229            }
230
231            // Update the props in ComponentInstance
232            component.props = props;
233
234            // Call after_update
235            {
236                let mut instance = component.instance.lock().map_err(|_| {
237                    ComponentError::LockError(
238                        "Failed to lock component instance for after_update".to_string(),
239                    )
240                })?;
241
242                instance.any_after_update()
243            }
244        };
245
246        if result.is_ok() {
247            // Update phase
248            self.phase = LifecyclePhase::Mounted;
249            self.context.set_lifecycle_phase(LifecyclePhase::Mounted);
250        }
251
252        result
253    }
254
255    /// Unmount the component from the tree
256    pub fn unmount(&mut self) -> Result<(), ComponentError> {
257        if self.phase != LifecyclePhase::Mounted {
258            return Err(ComponentError::InvalidLifecycleTransition(
259                self.phase,
260                "unmount".to_string(),
261            ));
262        }
263
264        // Before unmount phase
265        self.phase = LifecyclePhase::BeforeUnmount;
266        self.context
267            .set_lifecycle_phase(LifecyclePhase::BeforeUnmount);
268
269        // Execute before unmount hooks and call component's before_unmount
270        if let Ok(component_instance) = self.component.lock() {
271            let mut inner_component = component_instance.instance.lock().map_err(|_| {
272                ComponentError::LockError(
273                    "Failed to lock inner component for before_unmount".to_string(),
274                )
275            })?;
276
277            self.context
278                .execute_lifecycle_hooks(LifecyclePhase::BeforeUnmount, &mut **inner_component);
279
280            // Call the component's before_unmount method
281            inner_component.any_before_unmount()?;
282        } else {
283            return Err(ComponentError::LockError(
284                "Failed to lock component instance for before_unmount".to_string(),
285            ));
286        }
287
288        // Unmounting phase
289        self.phase = LifecyclePhase::Unmounting;
290        self.context.set_lifecycle_phase(LifecyclePhase::Unmounting);
291
292        // Create unmount context
293        let unmount_context = UnmountContext::new(
294            self.component
295                .lock()
296                .map_err(|_| {
297                    ComponentError::LockError(
298                        "Failed to lock component for unmount context".to_string(),
299                    )
300                })?
301                .instance
302                .lock()
303                .map_err(|_| {
304                    ComponentError::LockError(
305                        "Failed to lock inner component for unmount context".to_string(),
306                    )
307                })?
308                .component_id(),
309            UnmountReason::Removed,
310        );
311
312        let unmount_result = if let Ok(component_instance) = self.component.lock() {
313            let mut inner_component = component_instance.instance.lock().map_err(|_| {
314                ComponentError::LockError("Failed to lock inner component for unmount".to_string())
315            })?;
316
317            // Execute unmount hooks
318            self.context
319                .execute_lifecycle_hooks(LifecyclePhase::Unmounting, &mut **inner_component);
320
321            // Call the enhanced unmount method with context
322            inner_component.any_on_unmount(&unmount_context)?;
323
324            // Call the basic unmount method for backward compatibility
325            inner_component.any_unmount()
326        } else {
327            Err(ComponentError::LockError(
328                "Failed to lock component instance during unmounting".to_string(),
329            ))
330        };
331
332        if unmount_result.is_ok() {
333            // Update phase after successful unmount
334            self.phase = LifecyclePhase::Unmounted;
335            self.context.set_lifecycle_phase(LifecyclePhase::Unmounted);
336
337            // Call after_unmount hook
338            let after_unmount_result = if let Ok(component_instance) = self.component.lock() {
339                let mut inner_component = component_instance.instance.lock().map_err(|_| {
340                    ComponentError::LockError(
341                        "Failed to lock inner component for after_unmount".to_string(),
342                    )
343                })?;
344
345                inner_component.any_after_unmount()
346            } else {
347                Err(ComponentError::LockError(
348                    "Failed to lock component instance for after_unmount".to_string(),
349                ))
350            };
351
352            if let Err(e) = after_unmount_result {
353                // If after_unmount fails, we still consider the component unmounted but log the error
354                eprintln!("Warning: after_unmount failed for component: {e}");
355            }
356        }
357
358        unmount_result
359    }
360
361    /// Handle updates to the component
362    pub fn handle_updates(&mut self) -> Result<(), ComponentError> {
363        if self.phase != LifecyclePhase::Mounted {
364            return Err(ComponentError::InvalidLifecycleTransition(
365                self.phase,
366                "handle_updates".to_string(),
367            ));
368        }
369
370        // In a real implementation, this would check dirty state and update as needed
371        // For now, this is a placeholder that does nothing
372        Ok(())
373    }
374
375    /// Render the component
376    pub fn render(&self) -> Result<Vec<crate::component::Node>, ComponentError> {
377        if self.phase != LifecyclePhase::Mounted {
378            return Err(ComponentError::InvalidLifecycleTransition(
379                self.phase,
380                "render".to_string(),
381            ));
382        }
383
384        if let Ok(component_instance) = self.component.lock() {
385            let _inner_component = component_instance.instance.lock().map_err(|_| {
386                ComponentError::LockError("Failed to lock inner component for render".to_string())
387            })?;
388
389            // For now, return empty Vec since AnyComponent doesn't have render method
390            // In a real implementation, this would delegate to the Component trait's render method
391            Ok(vec![])
392        } else {
393            Err(ComponentError::LockError(
394                "Failed to lock component instance for rendering".to_string(),
395            ))
396        }
397    }
398
399    /// Get a reference to the component's context
400    pub fn get_context(&self) -> &Context {
401        &self.context
402    }
403}