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}