orbit/component/
context.rs

1//! Context passing and parent-child communication
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::sync::{Arc, RwLock};
7
8/// A type-erased value that can be stored in a context
9#[allow(dead_code)]
10pub trait ContextValue: Any + Send + Sync {
11    fn as_any(&self) -> &dyn Any;
12    fn as_any_mut(&mut self) -> &mut dyn Any;
13    fn box_clone(&self) -> Box<dyn ContextValue>;
14    fn debug_string(&self) -> String;
15}
16
17impl<T: Any + Clone + Send + Sync + Debug + 'static> ContextValue for T {
18    fn as_any(&self) -> &dyn Any {
19        self
20    }
21
22    fn as_any_mut(&mut self) -> &mut dyn Any {
23        self
24    }
25
26    fn box_clone(&self) -> Box<dyn ContextValue> {
27        Box::new(self.clone())
28    }
29    fn debug_string(&self) -> String {
30        format!("{self:?}")
31    }
32}
33
34/// Provider for component context
35#[derive(Clone, Default)]
36pub struct ContextProvider {
37    /// Parent context provider
38    parent: Option<Box<ContextProvider>>,
39    values: Arc<RwLock<HashMap<TypeId, Box<dyn ContextValue>>>>,
40}
41
42impl Debug for ContextProvider {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("ContextProvider")
45            .field("parent", &self.parent.is_some())
46            .field(
47                "values",
48                &format!(
49                    "[{} values]",
50                    self.values.read().map(|v| v.len()).unwrap_or(0)
51                ),
52            )
53            .finish()
54    }
55}
56
57impl ContextProvider {
58    /// Create a new context provider
59    pub fn new() -> Self {
60        Self {
61            parent: None,
62            values: Arc::new(RwLock::new(HashMap::new())),
63        }
64    }
65
66    /// Create a context provider with a parent
67    pub fn with_parent(parent: ContextProvider) -> Self {
68        Self {
69            parent: Some(Box::new(parent)),
70            values: Arc::new(RwLock::new(HashMap::new())),
71        }
72    }
73
74    /// Set a value in the context
75    pub fn provide<T: Clone + Send + Sync + std::fmt::Debug + 'static>(
76        &self,
77        value: T,
78    ) -> Result<(), String> {
79        let type_id = TypeId::of::<T>();
80        if let Ok(mut values) = self.values.write() {
81            values.insert(type_id, Box::new(value));
82            Ok(())
83        } else {
84            Err("Failed to acquire write lock for context values".to_string())
85        }
86    }
87
88    /// Get a value from the context
89    pub fn consume<T: Clone + Send + Sync + 'static>(&self) -> Option<T> {
90        let type_id = TypeId::of::<T>();
91
92        // Try to get from this context first
93        let result = {
94            if let Ok(values) = self.values.read() {
95                if let Some(value) = values.get(&type_id) {
96                    value.as_any().downcast_ref::<T>().cloned()
97                } else {
98                    None
99                }
100            } else {
101                None
102            }
103        };
104
105        // Return value from this context if found
106        if result.is_some() {
107            return result;
108        }
109
110        // Fall back to parent context if available
111        if let Some(parent) = &self.parent {
112            parent.consume::<T>()
113        } else {
114            None
115        }
116    }
117
118    /// Check if a type exists in the context
119    pub fn has<T: 'static>(&self) -> bool {
120        let type_id = TypeId::of::<T>();
121
122        // Check this context first
123        let exists_here = {
124            if let Ok(values) = self.values.read() {
125                values.contains_key(&type_id)
126            } else {
127                false
128            }
129        };
130
131        if exists_here {
132            return true;
133        }
134
135        // Check parent context if available
136        if let Some(parent) = &self.parent {
137            parent.has::<T>()
138        } else {
139            false
140        }
141    }
142
143    /// Remove a value from the context
144    pub fn remove<T: 'static>(&self) -> bool {
145        let type_id = TypeId::of::<T>();
146        let result = {
147            if let Ok(mut values) = self.values.write() {
148                values.remove(&type_id).is_some()
149            } else {
150                false
151            }
152        };
153        result
154    }
155}
156
157/// A callback function that can be passed as a prop
158pub struct Callback<Args, Ret = ()> {
159    /// The function to call
160    func: Arc<dyn Fn(Args) -> Ret + Send + Sync>,
161}
162
163impl<Args: 'static, Ret: 'static> Clone for Callback<Args, Ret> {
164    fn clone(&self) -> Self {
165        Self {
166            func: self.func.clone(),
167        }
168    }
169}
170
171impl<Args, Ret> Callback<Args, Ret> {
172    /// Create a new callback
173    pub fn new<F>(func: F) -> Self
174    where
175        F: Fn(Args) -> Ret + Send + Sync + 'static,
176    {
177        Self {
178            func: Arc::new(func),
179        }
180    }
181
182    /// Call the callback with the given arguments
183    pub fn call(&self, args: Args) -> Ret {
184        (self.func)(args)
185    }
186}
187
188/// Convenience function for creating a callback
189pub fn callback<F, Args, Ret>(func: F) -> Callback<Args, Ret>
190where
191    F: Fn(Args) -> Ret + Send + Sync + 'static,
192{
193    Callback::new(func)
194}