Skip to main content

ferroui_base/controls/
i_name_scope.rs

1use crate::utilities::{SynchronousCompletionAsyncResult, SynchronousCompletionAsyncResultSource};
2use crate::data::core::ValueTypes;
3use crate::{BoxedValue, FerroObject, Ref};
4use std::ops::Deref;
5use std::rc::Rc;
6
7/// Why an element could not be registered in a name scope.
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum NameScopeError {
10    /// The name scope is completed: no further registrations are allowed.
11    Completed,
12    /// Another element is registered with the name.
13    DuplicateName(String),
14}
15
16impl std::fmt::Display for NameScopeError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Self::Completed => f.write_str("NameScope is completed, no further registrations are allowed"),
20            Self::DuplicateName(name) => write!(f, "Control with the name '{name}' already registered."),
21        }
22    }
23}
24
25impl std::error::Error for NameScopeError {}
26
27/// Why a value could not be registered in a name scope
28/// ([`INameScope::try_register_value`]): the errors of [`NameScopeError`] and
29/// the ones only a value can have.
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub enum NameScopeValueError {
32    /// The name scope is completed: no further registrations are allowed.
33    Completed,
34    /// Another value is registered with the name.
35    DuplicateName(String),
36    /// A custom scope that only implements the registration of objects of the
37    /// object model was given another value.
38    UnsupportedValue,
39    /// The value is null (a boxed nullable that holds no value).
40    NullValue,
41}
42
43impl std::fmt::Display for NameScopeValueError {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            Self::Completed => NameScopeError::Completed.fmt(f),
47            Self::DuplicateName(name) => NameScopeError::DuplicateName(name.clone()).fmt(f),
48            Self::NullValue => f.write_str("Value cannot be null. (Parameter 'element')"),
49            Self::UnsupportedValue => f.write_str("This custom name scope only supports native objects."),
50        }
51    }
52}
53
54impl std::error::Error for NameScopeValueError {}
55
56impl From<NameScopeError> for NameScopeValueError {
57    fn from(error: NameScopeError) -> Self {
58        match error {
59            NameScopeError::Completed => Self::Completed,
60            NameScopeError::DuplicateName(name) => Self::DuplicateName(name),
61        }
62    }
63}
64
65/// Defines a name scope.
66pub trait INameScope {
67    /// Registers any non-null object, retaining its identity. The value form
68    /// corresponds to the managed `object` parameter. Existing native-only
69    /// custom scopes can override this method to accept plain objects too.
70    fn try_register_value(&self, name: &str, value: BoxedValue) -> Result<(), NameScopeValueError> {
71        if self.is_completed() {
72            return Err(NameScopeValueError::Completed);
73        }
74        let value = ValueTypes::normalize(value).ok_or(NameScopeValueError::NullValue)?;
75        let element = ValueTypes::as_object(&*value).ok_or(NameScopeValueError::UnsupportedValue)?;
76        Ok(self.try_register(name, element)?)
77    }
78
79    /// Registers any object; panics on the same errors as `try_register_value`.
80    fn register_value(&self, name: &str, value: BoxedValue) {
81        if let Err(error) = self.try_register_value(name, value) {
82            panic!("{error}");
83        }
84    }
85
86    /// Finds any named object immediately, without traversing incomplete scopes.
87    fn find_value(&self, name: &str) -> Option<BoxedValue> {
88        self.find(name).map(|element| Rc::new(element) as BoxedValue)
89    }
90
91    /// Finds any named object, completing synchronously on registration or completion.
92    fn find_value_async(&self, name: &str) -> SynchronousCompletionAsyncResult<Option<BoxedValue>> {
93        map_result(self.find_async(name), |element| element.map(|element| Rc::new(element) as BoxedValue))
94    }
95
96    /// Registers an element in the name scope. Panics if the scope is
97    /// completed or another element is registered with the name: see
98    /// [`try_register`](Self::try_register) for the form that reports these
99    /// as errors.
100    fn register(&self, name: &str, element: Ref<FerroObject>) {
101        if let Err(error) = self.try_register(name, element) {
102            panic!("{error}");
103        }
104    }
105
106    /// Registers an element in the name scope, reporting a completed scope
107    /// and a name that another element is registered with as errors.
108    /// Registering an element again with its name does nothing.
109    fn try_register(&self, name: &str, element: Ref<FerroObject>) -> Result<(), NameScopeError>;
110
111    /// Finds a named element in the name scope, waits for the scope to be
112    /// completely populated before returning `None`. Returned results always
113    /// complete synchronously.
114    fn find_async(&self, name: &str) -> SynchronousCompletionAsyncResult<Option<Ref<FerroObject>>>;
115
116    /// Finds a named element in the name scope, returns immediately, doesn't
117    /// traverse the name scope stack.
118    fn find(&self, name: &str) -> Option<Ref<FerroObject>>;
119
120    /// Marks the name scope as completed, no further registrations will be
121    /// allowed.
122    fn complete(&self);
123
124    /// Whether the name scope is completed, no further registrations are
125    /// allowed.
126    fn is_completed(&self) -> bool;
127
128    /// The scope became the value of the attached `NameScope` property of
129    /// `owner`, which holds it from now on. Not a member of the managed
130    /// original: there the scope and the element hold each other and the
131    /// collector frees both; here the scope holds this one element weakly
132    /// (it finds it as long as it is alive). A scope that holds no elements
133    /// itself has nothing to do.
134    fn attached_to(&self, _owner: &Ref<FerroObject>) {}
135
136    /// The scope is no longer the value of the attached `NameScope`
137    /// property of `owner`: it holds the element as every other again.
138    fn detached_from(&self, _owner: &Ref<FerroObject>) {}
139}
140
141/// Projects a result without holding a scope borrow across synchronous callbacks.
142pub(crate) fn map_result<T: Clone + 'static, U: Clone + 'static>(
143    result: SynchronousCompletionAsyncResult<T>,
144    map: impl FnOnce(T) -> U + 'static,
145) -> SynchronousCompletionAsyncResult<U> {
146    if result.is_completed() {
147        return SynchronousCompletionAsyncResult::new(map(result.get_result()));
148    }
149    let source = SynchronousCompletionAsyncResultSource::new();
150    let output = source.async_result();
151    let pending = result.clone();
152    result.on_completed(move || source.set_result(map(pending.get_result())));
153    output
154}
155
156/// A shared handle to a name scope, compared by identity so that it can be a
157/// property value.
158#[derive(Clone)]
159pub struct NameScopeRef(pub Rc<dyn INameScope>);
160
161impl NameScopeRef {
162    pub fn new(scope: impl INameScope + 'static) -> Self {
163        NameScopeRef(Rc::new(scope))
164    }
165}
166
167impl Deref for NameScopeRef {
168    type Target = dyn INameScope;
169
170    fn deref(&self) -> &(dyn INameScope + 'static) {
171        &*self.0
172    }
173}
174
175impl PartialEq for NameScopeRef {
176    fn eq(&self, other: &Self) -> bool {
177        std::ptr::addr_eq(Rc::as_ptr(&self.0), Rc::as_ptr(&other.0))
178    }
179}
180
181impl From<Rc<dyn INameScope>> for NameScopeRef {
182    fn from(value: Rc<dyn INameScope>) -> Self {
183        NameScopeRef(value)
184    }
185}
186
187/// Handles compare by identity (reference equality), so that they can be
188/// held in property and untyped values.
189impl PartialEq for dyn INameScope {
190    #[inline]
191    fn eq(&self, other: &Self) -> bool {
192        std::ptr::addr_eq(self as *const Self, other as *const Self)
193    }
194}