Skip to main content

ferroui_base/controls/
i_name_scope.rs

1use crate::utilities::SynchronousCompletionAsyncResult;
2use crate::{FerroObject, Ref};
3use std::ops::Deref;
4use std::rc::Rc;
5
6/// Why an element could not be registered in a name scope.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub enum NameScopeError {
9    /// The name scope is completed: no further registrations are allowed.
10    Completed,
11    /// Another element is registered with the name.
12    DuplicateName(String),
13}
14
15impl std::fmt::Display for NameScopeError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            Self::Completed => f.write_str("NameScope is completed, no further registrations are allowed"),
19            Self::DuplicateName(name) => write!(f, "Control with the name '{name}' already registered."),
20        }
21    }
22}
23
24impl std::error::Error for NameScopeError {}
25
26/// Defines a name scope.
27pub trait INameScope {
28    /// Registers an element in the name scope. Panics if the scope is
29    /// completed or another element is registered with the name: see
30    /// [`try_register`](Self::try_register) for the form that reports these
31    /// as errors.
32    fn register(&self, name: &str, element: Ref<FerroObject>) {
33        if let Err(error) = self.try_register(name, element) {
34            panic!("{error}");
35        }
36    }
37
38    /// Registers an element in the name scope, reporting a completed scope
39    /// and a name that another element is registered with as errors.
40    /// Registering an element again with its name does nothing.
41    fn try_register(&self, name: &str, element: Ref<FerroObject>) -> Result<(), NameScopeError>;
42
43    /// Finds a named element in the name scope, waits for the scope to be
44    /// completely populated before returning `None`. Returned results always
45    /// complete synchronously.
46    fn find_async(&self, name: &str) -> SynchronousCompletionAsyncResult<Option<Ref<FerroObject>>>;
47
48    /// Finds a named element in the name scope, returns immediately, doesn't
49    /// traverse the name scope stack.
50    fn find(&self, name: &str) -> Option<Ref<FerroObject>>;
51
52    /// Marks the name scope as completed, no further registrations will be
53    /// allowed.
54    fn complete(&self);
55
56    /// Whether the name scope is completed, no further registrations are
57    /// allowed.
58    fn is_completed(&self) -> bool;
59
60    /// The scope became the value of the attached `NameScope` property of
61    /// `owner`, which holds it from now on. Not a member of the managed
62    /// original: there the scope and the element hold each other and the
63    /// collector frees both; here the scope holds this one element weakly
64    /// (it finds it as long as it is alive). A scope that holds no elements
65    /// itself has nothing to do.
66    fn attached_to(&self, _owner: &Ref<FerroObject>) {}
67
68    /// The scope is no longer the value of the attached `NameScope`
69    /// property of `owner`: it holds the element as every other again.
70    fn detached_from(&self, _owner: &Ref<FerroObject>) {}
71}
72
73/// A shared handle to a name scope, compared by identity so that it can be a
74/// property value.
75#[derive(Clone)]
76pub struct NameScopeRef(pub Rc<dyn INameScope>);
77
78impl NameScopeRef {
79    pub fn new(scope: impl INameScope + 'static) -> Self {
80        NameScopeRef(Rc::new(scope))
81    }
82}
83
84impl Deref for NameScopeRef {
85    type Target = dyn INameScope;
86
87    fn deref(&self) -> &(dyn INameScope + 'static) {
88        &*self.0
89    }
90}
91
92impl PartialEq for NameScopeRef {
93    fn eq(&self, other: &Self) -> bool {
94        std::ptr::addr_eq(Rc::as_ptr(&self.0), Rc::as_ptr(&other.0))
95    }
96}
97
98impl From<Rc<dyn INameScope>> for NameScopeRef {
99    fn from(value: Rc<dyn INameScope>) -> Self {
100        NameScopeRef(value)
101    }
102}
103
104/// Handles compare by identity (reference equality), so that they can be
105/// held in property and untyped values.
106impl PartialEq for dyn INameScope {
107    #[inline]
108    fn eq(&self, other: &Self) -> bool {
109        std::ptr::addr_eq(self as *const Self, other as *const Self)
110    }
111}