Skip to main content

ferroui_base/metadata/
markup_type.rs

1//! Markup metadata: what markup (and other untyped code) needs to know
2//! about a type and cannot derive from the property system.
3//!
4//! The managed original answers these questions with reflection: which
5//! property holds the content of an element, which plain (non-registered)
6//! properties, collections and events a type has, how to construct it with
7//! arguments, how to parse it from text, which attributes it carries. Here a
8//! type states them once, as constant data, with the declaration forms of
9//! [`ferro_class_info!`](crate::ferro_class_info) (classes),
10//! [`ferro_markup_type!`](crate::ferro_markup_type) (every other type) and
11//! [`ferro_markup_enum!`](crate::ferro_markup_enum) (enumerations).
12//!
13//! Everything in this module is plain `'static` data and function pointers:
14//! declaring metadata costs nothing at run time until something reads it,
15//! and nothing is registered before `main`.
16//!
17//! # Values
18//!
19//! Members are invoked with untyped values ([`MarkupValue`]): `None` is
20//! null, `Some(box)` holds a value in its untyped form, the form
21//! [`ValueTypes`] produces for bindings (the contents of a nullable, the
22//! object itself for a reference type, the handle for a class instance).
23//! An invoker converts each argument to the declared Rust parameter type
24//! with the assignability casts of [`ValueTypes`] (identity, base class and
25//! interface handles, wrapping into `Option<T>`, boxing into the "any value"
26//! type); conversions that change the value (parsing, numeric conversion)
27//! are the caller's business.
28
29use crate::data::core::{ValueType, ValueTypes, WeakValue};
30use crate::reactive::IDisposable;
31use crate::{BoxedValue, TypeInfo};
32use std::any::{Any, TypeId};
33use std::collections::HashMap;
34use std::fmt;
35use std::rc::Rc;
36use std::sync::{OnceLock, RwLock};
37
38/// Names a Rust type lazily. Type identities cannot be computed in constant
39/// data, so metadata stores a function that returns them.
40pub type TypeOf = fn() -> ValueType;
41
42/// An untyped value passed to and returned from an invoker: `None` is null.
43pub type MarkupValue = Option<BoxedValue>;
44
45/// An untyped member: a constructor, method, property accessor or event
46/// subscription. For instance members the first argument is the instance.
47pub type MarkupInvoke = fn(&[MarkupValue]) -> Result<MarkupValue, MarkupInvokeError>;
48
49/// Why an untyped member could not be invoked.
50#[derive(Clone, Debug, PartialEq)]
51pub enum MarkupInvokeError {
52    /// The member was called with the wrong number of arguments (including
53    /// the instance).
54    ArgumentCount { expected: usize, actual: usize },
55    /// An argument is not assignable to the declared parameter type.
56    Argument { index: usize, expected: &'static str, actual: String },
57    /// The member itself failed (the equivalent of an exception thrown by
58    /// the member, for example a parse error).
59    Failed(String),
60}
61
62impl fmt::Display for MarkupInvokeError {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            Self::ArgumentCount { expected, actual } => {
66                write!(f, "Expected {expected} argument(s), got {actual}.")
67            }
68            Self::Argument { index, expected, actual } => {
69                write!(f, "Argument {index}: a value of type '{actual}' is not assignable to '{expected}'.")
70            }
71            Self::Failed(message) => f.write_str(message),
72        }
73    }
74}
75
76impl std::error::Error for MarkupInvokeError {}
77
78/// The declared method a [`MarkupDelegate`] was created from: what the
79/// `Target` and `Method` of a delegate are in the managed original.
80struct MarkupDelegateMethod {
81    target: DelegateTarget,
82    declaring_type: &'static MarkupType,
83    method: &'static MarkupMethod,
84}
85
86/// The instance the delegate of a declared method is bound to.
87#[derive(Clone)]
88enum DelegateTarget {
89    /// The instance, kept alive by the delegate; `None` for a static method.
90    Strong(MarkupValue),
91    /// The instance of the delegate of a binding source
92    /// ([`MarkupDelegate::for_method_of_source`]).
93    Weak(WeakValue),
94}
95
96impl DelegateTarget {
97    /// The instance; `Err` once an instance that is held weakly is gone.
98    fn get(&self) -> Result<MarkupValue, ()> {
99        match self {
100            DelegateTarget::Strong(target) => Ok(target.clone()),
101            DelegateTarget::Weak(target) => target.upgrade().map(Some).ok_or(()),
102        }
103    }
104}
105
106/// An untyped callback (the equivalent of a delegate instance): what an
107/// event subscription declared in metadata receives as its handler, and what
108/// a binding to a method produces.
109#[derive(Clone)]
110pub struct MarkupDelegate {
111    call: Rc<dyn Fn(&[MarkupValue]) -> MarkupValue>,
112    method: Option<Rc<MarkupDelegateMethod>>,
113}
114
115impl MarkupDelegate {
116    pub fn new(f: impl Fn(&[MarkupValue]) -> MarkupValue + 'static) -> Self {
117        Self { call: Rc::new(f), method: None }
118    }
119
120    /// The delegate of a method declared in metadata, bound to `target`
121    /// (`method.CreateDelegate(type, target)` in the managed original).
122    /// `target` is the instance for an instance method and ignored for a
123    /// static one; `declaring_type` is the type whose metadata lists
124    /// `method`. The delegate keeps the target alive.
125    ///
126    /// [`invoke`](Self::invoke) calls the method with the arguments given
127    /// (after the instance) and discards a failure;
128    /// [`try_invoke`](Self::try_invoke) reports it.
129    pub fn for_method(target: MarkupValue, declaring_type: &'static MarkupType, method: &'static MarkupMethod) -> Self {
130        let target = DelegateTarget::Strong(if method.is_static { None } else { target });
131        Self::bound(target, declaring_type, method)
132    }
133
134    /// The delegate of a method of a binding source: what a binding to a
135    /// method reads. As [`for_method`](Self::for_method), but the delegate
136    /// does not keep `target` alive (a source that is a reference; a plain
137    /// value is held as a binding holds it, by value). Once the target is
138    /// gone [`target`](Self::target) is `None`,
139    /// [`is_target_alive`](Self::is_target_alive) is false and invoking the
140    /// delegate does nothing.
141    ///
142    /// Deviation (DEVIATIONS.md, Bindings): the managed delegate has its
143    /// target strongly, and so has the command made from it. A template that
144    /// binds a command of one of its parts to a method of the templated
145    /// control is a cycle there (the control, its template, the part, the
146    /// value of its command property, the control), which the collector
147    /// frees; without one it would never be freed.
148    pub fn for_method_of_source(
149        target: &BoxedValue,
150        declaring_type: &'static MarkupType,
151        method: &'static MarkupMethod,
152    ) -> Self {
153        let target = match method.is_static {
154            true => DelegateTarget::Strong(None),
155            false => DelegateTarget::Weak(WeakValue::new(target)),
156        };
157        Self::bound(target, declaring_type, method)
158    }
159
160    fn bound(target: DelegateTarget, declaring_type: &'static MarkupType, method: &'static MarkupMethod) -> Self {
161        let bound = target.clone();
162        Self {
163            call: Rc::new(move |arguments| match bound.get() {
164                Ok(target) => invoke_method(method, &target, arguments).ok().flatten(),
165                Err(()) => None,
166            }),
167            method: Some(Rc::new(MarkupDelegateMethod { target, declaring_type, method })),
168        }
169    }
170
171    /// Calls the callback.
172    pub fn invoke(&self, arguments: &[MarkupValue]) -> MarkupValue {
173        (self.call)(arguments)
174    }
175
176    /// Calls the callback. For the delegate of a declared method
177    /// ([`for_method`](Self::for_method)) a failure of the call (wrong
178    /// arguments, a failing member) is returned.
179    pub fn try_invoke(&self, arguments: &[MarkupValue]) -> Result<MarkupValue, MarkupInvokeError> {
180        match &self.method {
181            Some(bound) => match bound.target.get() {
182                Ok(target) => invoke_method(bound.method, &target, arguments),
183                Err(()) => Ok(None),
184            },
185            None => Ok((self.call)(arguments)),
186        }
187    }
188
189    /// The instance the delegate of a declared instance method is bound to
190    /// (the `Target` of the managed original). `None` for a static method
191    /// and for a delegate that was not created from a declared method.
192    pub fn target(&self) -> MarkupValue {
193        self.method.as_ref().and_then(|bound| bound.target.get().ok().flatten())
194    }
195
196    /// Whether the delegate can still call what it was created for: false
197    /// once the target of the delegate of a binding source
198    /// ([`for_method_of_source`](Self::for_method_of_source)) is gone.
199    pub fn is_target_alive(&self) -> bool {
200        self.method.as_ref().is_none_or(|bound| bound.target.get().is_ok())
201    }
202
203    /// The declared method the delegate was created from (the `Method` of
204    /// the managed original), if it was created with
205    /// [`for_method`](Self::for_method).
206    pub fn method(&self) -> Option<&'static MarkupMethod> {
207        self.method.as_ref().map(|bound| bound.method)
208    }
209
210    /// The type whose metadata declares [`method`](Self::method).
211    pub fn declaring_type(&self) -> Option<&'static MarkupType> {
212        self.method.as_ref().map(|bound| bound.declaring_type)
213    }
214}
215
216fn invoke_method(
217    method: &'static MarkupMethod,
218    target: &MarkupValue,
219    arguments: &[MarkupValue],
220) -> Result<MarkupValue, MarkupInvokeError> {
221    if method.is_static {
222        return (method.invoke)(arguments);
223    }
224    let mut all = Vec::with_capacity(arguments.len() + 1);
225    all.push(target.clone());
226    all.extend(arguments.iter().cloned());
227    (method.invoke)(&all)
228}
229
230impl PartialEq for MarkupDelegate {
231    fn eq(&self, other: &Self) -> bool {
232        Rc::ptr_eq(&self.call, &other.call)
233    }
234}
235
236impl fmt::Debug for MarkupDelegate {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        f.write_str("MarkupDelegate")
239    }
240}
241
242/// The name of the delegate type, as the managed original prints a delegate
243/// (`System.Action`, ``System.Action`1[System.Int32]``,
244/// ``System.Func`2[System.Object,System.Boolean]``) for the delegate of a
245/// declared method; `System.Delegate` otherwise.
246impl fmt::Display for MarkupDelegate {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        let Some(method) = self.method() else {
249            return f.write_str("System.Delegate");
250        };
251        let mut types: Vec<String> =
252            method.parameters.iter().map(|parameter| ValueTypes::type_full_name(parameter())).collect();
253        let definition = match method.return_type {
254            Some(return_type) => {
255                types.push(ValueTypes::type_full_name(return_type()));
256                "System.Func"
257            }
258            None => "System.Action",
259        };
260        match types.len() {
261            0 => f.write_str(definition),
262            count => write!(f, "{definition}`{count}[{}]", types.join(",")),
263        }
264    }
265}
266
267/// Converts a typed value to its untyped form: null for `()` and for an
268/// empty nullable, the contents of a nullable, the object itself for a
269/// reference type, the value unchanged if it is already untyped.
270pub fn into_markup_value<T: PartialEq + 'static>(value: T) -> MarkupValue {
271    {
272        let any: &dyn Any = &value;
273        if let Some(untyped) = any.downcast_ref::<BoxedValue>() {
274            return Some(untyped.clone());
275        }
276        if let Some(untyped) = any.downcast_ref::<Option<BoxedValue>>() {
277            return untyped.clone();
278        }
279        if any.is::<()>() {
280            return None;
281        }
282    }
283    untyped_markup_value(Rc::new(value))
284}
285
286/// The untyped form of a boxed typed value (the part of
287/// [`into_markup_value`] that does not depend on the type of the value).
288fn untyped_markup_value(boxed: BoxedValue) -> MarkupValue {
289    match ValueTypes::try_cast(&boxed, ValueType::of::<Option<BoxedValue>>()) {
290        Some(untyped) => untyped.downcast_ref::<Option<BoxedValue>>().cloned().flatten(),
291        None => Some(boxed),
292    }
293}
294
295/// Converts an untyped value to the Rust type `T` with the assignability
296/// casts of [`ValueTypes`]. `None` if the value is not assignable to `T`.
297pub fn from_markup_value<T: Clone + 'static>(value: &MarkupValue) -> Option<T> {
298    let target = TypeId::of::<T>();
299    if target == TypeId::of::<Option<BoxedValue>>() {
300        let any: &dyn Any = value;
301        return any.downcast_ref::<T>().cloned();
302    }
303    match value {
304        Some(boxed) => {
305            if let Some(exact) = boxed.downcast_ref::<T>() {
306                return Some(exact.clone());
307            }
308            if target == TypeId::of::<BoxedValue>() {
309                let any: &dyn Any = boxed;
310                return any.downcast_ref::<T>().cloned();
311            }
312            if let Some(cast) = ValueTypes::try_cast(boxed, ValueType::of::<T>()) {
313                return cast.downcast_ref::<T>().cloned();
314            }
315            // A handle of a base class whose object is an instance of the class `T` names:
316            // an untyped call checks its arguments against the run-time class of the object
317            // (a binding reads a member of the class of the element it found through a
318            // handle of `Control` or of the root class).
319            let object = ValueTypes::as_object(&**boxed)?;
320            let cast = match ValueTypes::from_object(target, object.clone()) {
321                Some(cast) => cast,
322                None => ValueTypes::try_convert_registered(&(Rc::new(object) as BoxedValue), ValueType::of::<T>())?,
323            };
324            cast.downcast_ref::<T>().cloned()
325        }
326        None => {
327            let null = ValueTypes::try_convert(None, ValueType::of::<T>())??;
328            null.downcast_ref::<T>().cloned()
329        }
330    }
331}
332
333/// Maps the error of a fallible member to [`MarkupInvokeError::Failed`]; see
334/// the `try` forms of [`ferro_markup_type!`](crate::ferro_markup_type).
335pub fn markup_result<T, E: fmt::Display>(result: Result<T, E>) -> Result<T, MarkupInvokeError> {
336    result.map_err(|error| MarkupInvokeError::Failed(error.to_string()))
337}
338
339/// The arguments of an untyped call, read in order by a generated invoker.
340pub struct MarkupArguments<'a> {
341    arguments: &'a [MarkupValue],
342    next: usize,
343}
344
345impl<'a> MarkupArguments<'a> {
346    /// Checks the number of arguments.
347    pub fn new(arguments: &'a [MarkupValue], expected: usize) -> Result<Self, MarkupInvokeError> {
348        if arguments.len() != expected {
349            return Err(MarkupInvokeError::ArgumentCount { expected, actual: arguments.len() });
350        }
351        Ok(Self { arguments, next: 0 })
352    }
353
354    /// The next argument, as a `T`.
355    pub fn next<T: Clone + 'static>(&mut self) -> Result<T, MarkupInvokeError> {
356        let index = self.next;
357        self.next += 1;
358        let value = &self.arguments[index];
359        from_markup_value::<T>(value).ok_or_else(|| Self::argument_error(index, std::any::type_name::<T>(), value))
360    }
361
362    /// The error of an argument that is not assignable to the type asked for.
363    #[cold]
364    fn argument_error(index: usize, expected: &'static str, value: &MarkupValue) -> MarkupInvokeError {
365        MarkupInvokeError::Argument {
366            index,
367            expected,
368            actual: match value {
369                Some(value) => value.type_name().to_string(),
370                None => "null".to_string(),
371            },
372        }
373    }
374}
375
376/// What kind of type a [`MarkupType`] describes.
377#[derive(Clone, Copy, Debug, PartialEq, Eq)]
378pub enum MarkupTypeKind {
379    /// A reference type: a class of the object model or a plain shared type.
380    Class,
381    /// A value type.
382    Struct,
383    /// An enumeration; [`MarkupType::enum_members`] lists its members.
384    Enum,
385    /// A contract (a trait used through a handle).
386    Interface,
387    /// A type that cannot be instantiated and only owns static members.
388    Static,
389}
390
391/// A constant argument of a [`MarkupAttribute`].
392#[derive(Clone, Copy)]
393pub enum MarkupAttributeValue {
394    Null,
395    Bool(bool),
396    Int(i64),
397    Float(f64),
398    Str(&'static str),
399    Type(TypeOf),
400    /// An array argument (`new[] { ",", " " }` of the managed original).
401    Array(&'static [MarkupAttributeValue]),
402}
403
404impl fmt::Debug for MarkupAttributeValue {
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        match self {
407            Self::Null => f.write_str("null"),
408            Self::Bool(value) => write!(f, "{value}"),
409            Self::Int(value) => write!(f, "{value}"),
410            Self::Float(value) => write!(f, "{value:?}"),
411            Self::Str(value) => write!(f, "{value:?}"),
412            Self::Type(type_) => write!(f, "type({})", type_()),
413            Self::Array(items) => f.debug_list().entries(items.iter()).finish(),
414        }
415    }
416}
417
418impl PartialEq for MarkupAttributeValue {
419    fn eq(&self, other: &Self) -> bool {
420        match (self, other) {
421            (Self::Null, Self::Null) => true,
422            (Self::Bool(a), Self::Bool(b)) => a == b,
423            (Self::Int(a), Self::Int(b)) => a == b,
424            (Self::Float(a), Self::Float(b)) => a == b,
425            (Self::Str(a), Self::Str(b)) => a == b,
426            (Self::Type(a), Self::Type(b)) => a() == b(),
427            (Self::Array(a), Self::Array(b)) => a == b,
428            _ => false,
429        }
430    }
431}
432
433/// Turns a literal of an attribute declaration into a
434/// [`MarkupAttributeValue`] in constant context; see
435/// [`ferro_markup_type!`](crate::ferro_markup_type).
436#[doc(hidden)]
437pub struct MarkupLiteral<T>(pub T);
438
439impl MarkupLiteral<&'static str> {
440    pub const fn value(self) -> MarkupAttributeValue {
441        MarkupAttributeValue::Str(self.0)
442    }
443}
444
445impl MarkupLiteral<bool> {
446    pub const fn value(self) -> MarkupAttributeValue {
447        MarkupAttributeValue::Bool(self.0)
448    }
449}
450
451impl MarkupLiteral<i64> {
452    pub const fn value(self) -> MarkupAttributeValue {
453        MarkupAttributeValue::Int(self.0)
454    }
455}
456
457impl MarkupLiteral<f64> {
458    pub const fn value(self) -> MarkupAttributeValue {
459        MarkupAttributeValue::Float(self.0)
460    }
461}
462
463impl MarkupLiteral<char> {
464    pub const fn value(self) -> MarkupAttributeValue {
465        MarkupAttributeValue::Int(self.0 as i64)
466    }
467}
468
469/// An attribute of a type or member: the data of a custom attribute of the
470/// managed original (`[DependsOn("Property")]`,
471/// `[TemplateContent(TemplateResultType = typeof(..))]`).
472///
473/// `name` is the attribute name without the `Attribute` suffix; the names of
474/// the attributes the framework itself understands are the constants of
475/// [`attributes`](super::attributes).
476#[derive(Clone, Copy, Debug, PartialEq)]
477pub struct MarkupAttribute {
478    pub name: &'static str,
479    /// The constructor arguments.
480    pub arguments: &'static [MarkupAttributeValue],
481    /// The named arguments.
482    pub properties: &'static [(&'static str, MarkupAttributeValue)],
483}
484
485impl MarkupAttribute {
486    /// The named argument `name`.
487    pub fn property(&self, name: &str) -> Option<MarkupAttributeValue> {
488        self.properties.iter().find(|(n, _)| *n == name).map(|(_, value)| *value)
489    }
490}
491
492/// The name and attributes of a parameter of a [`MarkupConstructor`]
493/// (`[InheritDataTypeFrom(..)] FerroProperty property` of the managed
494/// original).
495#[derive(Clone, Copy, Debug, PartialEq)]
496pub struct MarkupParameter {
497    /// The parameter name, when the declaration states it.
498    pub name: Option<&'static str>,
499    pub attributes: &'static [MarkupAttribute],
500}
501
502impl MarkupConstructor {
503    /// The declared name of parameter `index`.
504    pub fn parameter_name(&self, index: usize) -> Option<&'static str> {
505        self.parameter_info.get(index).and_then(|parameter| parameter.name)
506    }
507
508    /// The declared attributes of parameter `index`.
509    pub fn parameter_attributes(&self, index: usize) -> &'static [MarkupAttribute] {
510        self.parameter_info.get(index).map_or(&[], |parameter| parameter.attributes)
511    }
512}
513
514/// A constructor with arguments. (The parameterless constructor of a class
515/// is its [`TypeInfo::default_constructor`]; other types list it here with
516/// no parameters.)
517#[derive(Clone, Copy)]
518pub struct MarkupConstructor {
519    pub parameters: &'static [TypeOf],
520    /// What the declaration states about each parameter beyond its type
521    /// (its name and attributes), by index. Empty when the constructor is
522    /// declared in the positional form `(A, B) => callable`, otherwise as
523    /// long as [`parameters`](Self::parameters).
524    pub parameter_info: &'static [MarkupParameter],
525    /// Creates the instance from the arguments.
526    pub invoke: MarkupInvoke,
527    /// The typed function the declaration generated for the constructor
528    /// (see [`MarkupEmit`]): `__markup_new_<n>(a0: A, ..) -> T`, where `T` is
529    /// the value type of the declared type ([`MarkupType::value`]).
530    pub emit: CompilerMetadata<Option<MarkupEmit>>,
531}
532
533/// A property that is not a registered property: a plain property with a
534/// getter and/or a setter. Collections are read-only properties whose type
535/// has an `Add` method.
536#[derive(Clone, Copy)]
537pub struct MarkupProperty {
538    pub name: &'static str,
539    pub type_: TypeOf,
540    /// Reads the property: `(instance) -> value`.
541    pub get: Option<MarkupInvoke>,
542    /// Writes the property: `(instance, value)`.
543    pub set: Option<MarkupInvoke>,
544    pub attributes: &'static [MarkupAttribute],
545    /// Adds the TYPED element of this property to a compiled binding path
546    /// built at run time (what `CompiledBindingPathBuilder::typed_property`
547    /// adds in generated code), so that a binding whose path is this single
548    /// property is a typed binding expression. Generated by the declaration
549    /// forms from the accessors; the hook returns `None` (and the caller
550    /// uses the untyped element) unless the instance type of the declaration
551    /// is a shared reference type `Rc<S>` (notifying or not). `None` for a static
552    /// property and for a property with a fallible setter. See
553    /// [`typed_path`](super::typed_path).
554    pub typed_path_element: Option<super::typed_path::TypedPathElement>,
555    /// The typed accessor functions the declaration generated for the
556    /// getter and the setter (see [`MarkupEmit`]); `None` for an accessor
557    /// that is not declared.
558    pub emit_get: CompilerMetadata<Option<MarkupEmit>>,
559    pub emit_set: CompilerMetadata<Option<MarkupEmit>>,
560}
561
562/// What only the emitter of Rust source reads (the typed functions of
563/// [`MarkupEmit`], the instance and value types of a declaration): the value
564/// itself with the `compiler-metadata` feature, a zero-sized [`NotRecorded`]
565/// without it, so that shipped applications carry neither the data nor the
566/// code the declaration macros would generate for it.
567#[cfg(feature = "compiler-metadata")]
568pub type CompilerMetadata<T> = T;
569
570/// What only the emitter of Rust source reads; see the other definition.
571#[cfg(not(feature = "compiler-metadata"))]
572pub type CompilerMetadata<T> = NotRecorded<T>;
573
574/// The zero-sized stand-in of [`CompilerMetadata`] without the
575/// `compiler-metadata` feature: nothing is recorded.
576pub struct NotRecorded<T>(std::marker::PhantomData<fn() -> T>);
577
578impl<T> NotRecorded<T> {
579    /// Nothing.
580    pub const fn new() -> Self {
581        Self(std::marker::PhantomData)
582    }
583}
584
585impl<T> Clone for NotRecorded<T> {
586    fn clone(&self) -> Self {
587        *self
588    }
589}
590
591impl<T> Copy for NotRecorded<T> {}
592
593impl<T> fmt::Debug for NotRecorded<T> {
594    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595        f.write_str("NotRecorded")
596    }
597}
598
599/// The value of a [`CompilerMetadata`] slot a declaration leaves empty.
600#[cfg(feature = "compiler-metadata")]
601pub const fn not_recorded<T>() -> CompilerMetadata<Option<T>> {
602    None
603}
604
605/// The value of a [`CompilerMetadata`] slot a declaration leaves empty.
606#[cfg(not(feature = "compiler-metadata"))]
607pub const fn not_recorded<T>() -> CompilerMetadata<Option<T>> {
608    NotRecorded::new()
609}
610
611/// A typed function the declaration forms generate for a declared member,
612/// for the emitter of Rust source: an associated function of the declared
613/// type (`#[doc(hidden)] pub fn __markup_get_Child(this: &Ref<Border>) -> ..`),
614/// public wherever the type is, that calls the declared callable with
615/// exactly the declared types. Generated code calls it by the public path
616/// of the type and `function`, whatever form the callable has (a path or a
617/// closure).
618///
619/// | Member | Function |
620/// |---|---|
621/// | property getter / setter | `__markup_get_<Name>(this: &This) -> T` / `__markup_set_<Name>(this: &This, value: T)` |
622/// | static property getter / setter | `__markup_static_get_<Name>() -> T` / `__markup_static_set_<Name>(value: T)` |
623/// | method (instance or static), in declaration order `n` | `__markup_<Name>_<n>([this: &This,] a0: A, ..) -> R` |
624/// | constructor, in declaration order `n` | `__markup_new_<n>(a0: A, ..) -> T` |
625/// | static field | `__markup_field_<Name>() -> T` |
626/// | `Parse(string)` | `__markup_parse(text: String) -> Result<T, MarkupInvokeError>` |
627///
628/// A fallible member (`try`) returns `Result<T, MarkupInvokeError>`
629/// ([`markup_result`] of the callable's result).
630#[derive(Clone, Copy, Debug, PartialEq, Eq)]
631pub struct MarkupEmit {
632    /// The name of the associated function.
633    pub function: &'static str,
634    /// Whether the function returns `Result<_, MarkupInvokeError>`.
635    pub fallible: bool,
636}
637
638/// An indexer (`this[..]` of the managed original): a property with index
639/// parameters. Binding paths reach it with `Path[0]` / `Path[key]`.
640#[derive(Clone, Copy)]
641pub struct MarkupIndexer {
642    /// The types of the index parameters, in order.
643    pub parameters: &'static [TypeOf],
644    /// The type of the values of the indexer.
645    pub type_: TypeOf,
646    /// Reads the value: `(instance, index..) -> value`.
647    pub get: Option<MarkupInvoke>,
648    /// Writes the value: `(instance, index.., value)`.
649    pub set: Option<MarkupInvoke>,
650    pub attributes: &'static [MarkupAttribute],
651}
652
653/// A method: an instance method (`Add`, a command method, an event handler)
654/// or a static one (`Parse`, a factory method, an attached property
655/// accessor).
656#[derive(Clone, Copy)]
657pub struct MarkupMethod {
658    pub name: &'static str,
659    pub is_static: bool,
660    /// The parameter types, without the instance.
661    pub parameters: &'static [TypeOf],
662    /// `None` for a method that returns nothing.
663    pub return_type: Option<TypeOf>,
664    /// Calls the method: `(instance, arguments..)`, or `(arguments..)` for
665    /// a static method.
666    pub invoke: MarkupInvoke,
667    pub attributes: &'static [MarkupAttribute],
668    /// The typed function the declaration generated for the method (see
669    /// [`MarkupEmit`]).
670    pub emit: CompilerMetadata<Option<MarkupEmit>>,
671}
672
673/// A static field of the managed original: a `static readonly` field or a
674/// constant (`Button.ClickEvent`, `ObjectConverters.IsNull`). A static
675/// PROPERTY (`static T Name { get; }`: `Brushes.Red`) is declared in
676/// [`MarkupType::static_properties`].
677#[derive(Clone, Copy)]
678pub struct MarkupField {
679    pub name: &'static str,
680    pub type_: TypeOf,
681    pub get: fn() -> MarkupValue,
682    pub attributes: &'static [MarkupAttribute],
683    /// The typed function of the field, for the emitter of Rust source.
684    pub emit: CompilerMetadata<Option<MarkupEmit>>,
685}
686
687/// An event that is not a routed event: handlers are attached through
688/// `add` (or [`subscribe`](Self::subscribe)).
689#[derive(Clone, Copy)]
690pub struct MarkupEvent {
691    pub name: &'static str,
692    /// The types of the arguments the event passes to its handlers
693    /// (sender first, where the event has one).
694    pub arguments: &'static [TypeOf],
695    /// Subscribes a handler: `(instance, handler)`, where the handler is a
696    /// [`MarkupDelegate`] called with the event arguments in untyped form.
697    /// Returns the subscription as an untyped `Rc<dyn IDisposable>`
698    /// ([`subscription_of`](Self::subscription_of)), or null when the event
699    /// offers no way to remove the handler.
700    pub add: MarkupInvoke,
701}
702
703impl MarkupEvent {
704    /// Attaches `handler` to the event of `target` (the `add` accessor of the
705    /// managed event, `EventInfo.AddEventHandler`) and returns the
706    /// subscription: disposing it removes the handler (the `remove`
707    /// accessor, `EventInfo.RemoveEventHandler`). `Ok(None)` when the event
708    /// declares no way to remove a handler.
709    ///
710    /// `target` is the instance in untyped form, as for every member
711    /// invoker. The error is the one of `add`: a target that is not an
712    /// instance of the declaring type, or the failure of a fallible
713    /// subscription.
714    pub fn subscribe(
715        &self,
716        target: MarkupValue,
717        handler: MarkupDelegate,
718    ) -> Result<Option<Rc<dyn IDisposable>>, MarkupInvokeError> {
719        let handler: BoxedValue = Rc::new(handler);
720        let result = (self.add)(&[target, Some(handler)])?;
721        Ok(Self::subscription_of(&result))
722    }
723
724    /// The subscription in the result of `add`: the untyped
725    /// `Rc<dyn IDisposable>` the declaration boxed, `None` for null.
726    pub fn subscription_of(value: &MarkupValue) -> Option<Rc<dyn IDisposable>> {
727        value.as_ref()?.downcast_ref::<Rc<dyn IDisposable>>().cloned()
728    }
729
730    /// The result of `add` for what a subscription function returned: the
731    /// disposable boxed as an untyped value, null for none. Used by the
732    /// `events:` declarations of the metadata macros, and by a hand-written
733    /// `add`.
734    pub fn subscription_value(subscription: impl IntoMarkupSubscription) -> MarkupValue {
735        subscription.into_markup_subscription().map(|subscription| Rc::new(subscription) as BoxedValue)
736    }
737}
738
739/// What the subscription function of an event declaration may return: a
740/// disposable that removes the handler, or nothing (`()`) for an event that
741/// offers no way to remove a handler.
742pub trait IntoMarkupSubscription {
743    /// The subscription as a shared disposable, if there is one.
744    fn into_markup_subscription(self) -> Option<Rc<dyn IDisposable>>;
745}
746
747impl IntoMarkupSubscription for () {
748    fn into_markup_subscription(self) -> Option<Rc<dyn IDisposable>> {
749        None
750    }
751}
752
753impl IntoMarkupSubscription for Rc<dyn IDisposable> {
754    fn into_markup_subscription(self) -> Option<Rc<dyn IDisposable>> {
755        Some(self)
756    }
757}
758
759impl<T: IDisposable + 'static> IntoMarkupSubscription for Rc<T> {
760    fn into_markup_subscription(self) -> Option<Rc<dyn IDisposable>> {
761        Some(self)
762    }
763}
764
765impl IntoMarkupSubscription for Option<Rc<dyn IDisposable>> {
766    fn into_markup_subscription(self) -> Option<Rc<dyn IDisposable>> {
767        self
768    }
769}
770
771/// A member of an enumeration.
772#[derive(Clone, Copy)]
773pub struct MarkupEnumMember {
774    pub name: &'static str,
775    /// The numeric value (the bits, for a flags enumeration).
776    pub value: i64,
777    /// The member as a value of the enumeration type.
778    pub get: fn() -> BoxedValue,
779    /// The identifier of the Rust variant of a plain enumeration (the member
780    /// name unless the declaration renames it), for the emitter of Rust
781    /// source: the member is `<public path of the type>::<rust_variant>`.
782    /// `None` for the members of a set of flags (their constants are
783    /// arbitrary expressions).
784    pub rust_variant: Option<&'static str>,
785}
786
787/// States that a type is an instantiation of a generic type
788/// (`FerroList<T>`): the name of the definition with its arity, as the
789/// managed original spells it (``FerroList`1``), and the type arguments.
790#[derive(Clone, Copy)]
791pub struct MarkupGeneric {
792    pub definition: &'static str,
793    pub arguments: &'static [TypeOf],
794}
795
796/// The markup metadata of a type. Built by the declaration macros as
797/// constant data; see the [module documentation](self).
798///
799/// A `MarkupType` is built only through [`MarkupType::new`] and the struct
800/// update syntax (`MarkupType { events: .., ..MarkupType::new(name, kind, module_path!()) }`):
801/// its fields grow with the compiler metadata of the markup compiler, and an
802/// added field is not a breaking change of the API (docs/release.md, "API
803/// stability").
804#[derive(Clone, Copy)]
805pub struct MarkupType {
806    /// The type name, without namespace, as markup spells it.
807    pub name: &'static str,
808    /// The dotted namespace, when it is not the namespace registered for
809    /// [`module_path`](Self::module_path) (types that mirror runtime library
810    /// types state `"System"` here).
811    pub explicit_namespace: Option<&'static str>,
812    /// The Rust module that declares the type (`module_path!()`).
813    pub module_path: &'static str,
814    pub kind: MarkupTypeKind,
815    /// The Rust types values of this type are held in, the untyped
816    /// (canonical) form first: `[Ref<T>, Option<Ref<T>>]` for a class,
817    /// `[T, Rc<T>, Option<Rc<T>>]` for a reference type, `[T]` for a
818    /// value type (its nullable form `Option<T>` is a type of its own, as
819    /// in the managed original), `[Rc<dyn I>, Option<Rc<dyn I>>]` for a
820    /// contract. Empty for static types.
821    pub handles: &'static [TypeOf],
822    /// The nullable form of a value type or enumeration (`Option<T>`), which
823    /// is a type of its own (the `Nullable<T>` of the managed original).
824    /// `None` for every other kind of type.
825    pub nullable: Option<TypeOf>,
826    /// The class of the object model this metadata belongs to, if any.
827    pub type_info: Option<fn() -> &'static TypeInfo>,
828    /// The base type (by one of its handles). Classes of the object model
829    /// leave this empty: their base is [`TypeInfo::base_type`].
830    pub base: Option<TypeOf>,
831    /// The contracts the type implements (by their handles), in addition
832    /// to the ones of its base type.
833    pub interfaces: &'static [TypeOf],
834    pub generic: Option<MarkupGeneric>,
835    /// The name of the content property (the `[Content]` attribute of the
836    /// managed original). Inherited by deriving types.
837    pub content_property: Option<&'static str>,
838    /// Converts text to a value of the type: `(text) -> value`. The
839    /// `Parse(string)` method of the managed original.
840    pub parse: Option<MarkupInvoke>,
841    pub constructors: &'static [MarkupConstructor],
842    pub properties: &'static [MarkupProperty],
843    /// The indexers the type declares (not the ones of its base types), in
844    /// declaration order.
845    pub indexers: &'static [MarkupIndexer],
846    /// Attributes of REGISTERED properties (styled, direct, attached) the
847    /// type declares or owns, by property name. Registered properties need
848    /// no declaration; this only carries what the property system does not
849    /// know about them (`[DependsOn]`, `[ResolveByName]`,
850    /// `[InheritDataTypeFromItems]`, `[TemplateContent]`, ...).
851    pub property_attributes: &'static [(&'static str, &'static [MarkupAttribute])],
852    pub methods: &'static [MarkupMethod],
853    /// The static fields and constants (`static readonly` / `const` of the
854    /// managed original).
855    pub fields: &'static [MarkupField],
856    /// The static properties (`static T Name { get; set; }` of the managed
857    /// original): the accessors take no instance, `get` is `() -> value`
858    /// and `set` is `(value)`.
859    pub static_properties: &'static [MarkupProperty],
860    pub events: &'static [MarkupEvent],
861    pub enum_members: &'static [MarkupEnumMember],
862    /// Whether an enumeration is a set of flags.
863    pub is_flags: bool,
864    /// Converts a numeric value to a value of the enumeration: the cast
865    /// `(TEnum)value` of the managed original. For a plain enumeration the
866    /// value must be the value of a member; for a set of flags any
867    /// combination of the bits of its members.
868    pub enum_from_value: Option<fn(i64) -> Option<BoxedValue>>,
869    /// The numeric value of a value of the enumeration: the cast
870    /// `(int)value` of the managed original. `None` for a value that is not
871    /// of the enumeration type.
872    pub enum_to_value: Option<fn(&dyn crate::AnyValue) -> Option<i64>>,
873    /// Explicit associated function `fn(i32) -> enum value` on the public metadata
874    /// carrier, for unnamed numeric literals of an open plain enumeration.
875    /// Declares support for every signed Int32 value; absent for closed enums.
876    /// Compiler metadata names the typed function; it never guesses a constructor.
877    pub enum_i32_factory: CompilerMetadata<Option<&'static str>>,
878    pub attributes: &'static [MarkupAttribute],
879    /// Views an untyped value of this type as a source of property change
880    /// notifications (the `INotifyPropertyChanged` of the managed original),
881    /// for types that are not classes of the object model: view models.
882    /// Bindings resolved through metadata (reflection bindings, compiled
883    /// bindings built at run time) subscribe through it; `None` means the
884    /// properties of the type are read once.
885    pub notify_property_changed:
886        Option<fn(&dyn crate::AnyValue) -> Option<&dyn crate::data::model::INotifyPropertyChanged>>,
887    /// The Rust type instance members receive (`&This`): the `this:` of the
888    /// declaration, else the declared type (`Ref<Class>` for a class). The
889    /// typed functions of the instance members ([`MarkupEmit`]) take it.
890    pub this: CompilerMetadata<Option<TypeOf>>,
891    /// The Rust type of a value of the type as its constructors and its
892    /// `Parse` return it, and so their typed functions ([`MarkupEmit`]):
893    /// the instance type when the declaration states `this:`, else the
894    /// first of its handles, else the declared type (`Rc<dyn Trait>` for a
895    /// contract). `Ref<Class>` for a class.
896    pub value: CompilerMetadata<Option<TypeOf>>,
897    /// The actual Rust declaration that owns the typed metadata functions.
898    /// It can differ from `this`/`value` for external-model descriptor carriers.
899    /// Compiler-only, like the typed function and public-path tables.
900    pub owner_rust_name: CompilerMetadata<Option<fn() -> &'static str>>,
901}
902
903impl MarkupType {
904    /// Metadata that states nothing but the name and kind of the type.
905    pub const fn new(name: &'static str, kind: MarkupTypeKind, module_path: &'static str) -> Self {
906        Self {
907            name,
908            explicit_namespace: None,
909            module_path,
910            kind,
911            handles: &[],
912            nullable: None,
913            type_info: None,
914            base: None,
915            interfaces: &[],
916            generic: None,
917            content_property: None,
918            parse: None,
919            constructors: &[],
920            properties: &[],
921            indexers: &[],
922            property_attributes: &[],
923            methods: &[],
924            fields: &[],
925            static_properties: &[],
926            events: &[],
927            enum_members: &[],
928            is_flags: false,
929            enum_from_value: None,
930            enum_to_value: None,
931            enum_i32_factory: not_recorded(),
932            attributes: &[],
933            notify_property_changed: None,
934            this: not_recorded(),
935            value: not_recorded(),
936            owner_rust_name: not_recorded(),
937        }
938    }
939
940    #[cfg(feature = "compiler-metadata")]
941    /// Declares the public Rust paths of markup types: the metadata of a
942    /// type, the path another crate names the type by
943    /// (`"ferroui_base::layout::HorizontalAlignment"`) and whether the path
944    /// names a trait (a contract, whose values are `Rc<dyn Trait>`). Called
945    /// from the `register_types()` of a crate with the table of its
946    /// generated `rust_paths.rs` ([`ferro_rust_paths!`](crate::ferro_rust_paths)).
947    /// The emitter of Rust source reads the table; nothing else does.
948    pub fn register_rust_paths(paths: &[(&'static MarkupType, &'static str, bool)]) {
949        let mut table = rust_paths().write().unwrap_or_else(|e| e.into_inner());
950        for &(markup, path, is_trait) in paths {
951            table.insert(markup as *const MarkupType as usize, (markup, path, is_trait));
952        }
953    }
954
955    #[cfg(feature = "compiler-metadata")]
956    /// The public Rust path of the type
957    /// ([`register_rust_paths`](Self::register_rust_paths)), if its crate
958    /// recorded one; for a contract, the path of the trait.
959    pub fn rust_path(&'static self) -> Option<&'static str> {
960        self.rust_path_entry().map(|entry| entry.1)
961    }
962
963    #[cfg(feature = "compiler-metadata")]
964    /// Whether the recorded public Rust path names a trait.
965    pub fn rust_path_is_trait(&'static self) -> bool {
966        self.rust_path_entry().is_some_and(|entry| entry.2)
967    }
968
969    #[cfg(feature = "compiler-metadata")]
970    fn rust_path_entry(&'static self) -> Option<(&'static MarkupType, &'static str, bool)> {
971        rust_paths().read().unwrap_or_else(|e| e.into_inner()).get(&(self as *const MarkupType as usize)).copied()
972    }
973
974    #[cfg(feature = "compiler-metadata")]
975    /// Every markup type with a registered public Rust path, with the path
976    /// and whether it names a trait, in no particular order.
977    pub fn all_rust_paths() -> Vec<(&'static MarkupType, &'static str, bool)> {
978        rust_paths().read().unwrap_or_else(|e| e.into_inner()).values().copied().collect()
979    }
980
981    /// The dotted, markup-facing namespace of the type.
982    pub fn namespace(&self) -> &'static str {
983        match self.explicit_namespace {
984            Some(namespace) => namespace,
985            None => TypeInfo::namespace_of_module(self.module_path),
986        }
987    }
988
989    /// The namespace-qualified name of the type.
990    pub fn full_name(&self) -> String {
991        match self.namespace() {
992            "" => self.name.to_string(),
993            namespace => format!("{namespace}.{}", self.name),
994        }
995    }
996
997    /// The untyped (canonical) handle type of the type, if it has values.
998    pub fn handle(&self) -> Option<ValueType> {
999        self.handles.first().map(|handle| handle())
1000    }
1001
1002    /// The plain property `name` declared by this type (not by its base
1003    /// types).
1004    pub fn find_property(&self, name: &str) -> Option<&'static MarkupProperty> {
1005        self.properties.iter().find(|p| p.name == name)
1006    }
1007
1008    /// The indexer this type declares (not its base types) that takes
1009    /// `parameter_count` index arguments.
1010    pub fn find_indexer(&self, parameter_count: usize) -> Option<&'static MarkupIndexer> {
1011        self.indexers.iter().find(|indexer| indexer.parameters.len() == parameter_count)
1012    }
1013
1014    /// The registered metadata of the base type ([`base`](Self::base)): for
1015    /// a class of the object model the metadata of the nearest base class
1016    /// that has any, otherwise the registered type that the base handle
1017    /// belongs to. This is how untyped code walks the hierarchy of a type,
1018    /// as reflection walks base classes.
1019    pub fn base_type(&self) -> Option<&'static MarkupType> {
1020        if let Some(type_info) = self.type_info {
1021            let mut current = type_info().base_type();
1022            while let Some(class) = current {
1023                if let Some(markup) = class.markup() {
1024                    return Some(markup);
1025                }
1026                current = class.base_type();
1027            }
1028        }
1029        Self::find_by_handle(self.base?().id())
1030    }
1031
1032    /// The attributes this type declares for its registered property `name`.
1033    pub fn find_property_attributes(&self, name: &str) -> &'static [MarkupAttribute] {
1034        self.property_attributes.iter().find(|(n, _)| *n == name).map_or(&[], |(_, attributes)| *attributes)
1035    }
1036
1037    /// The methods named `name` declared by this type.
1038    pub fn find_methods<'a>(&self, name: &'a str) -> impl Iterator<Item = &'static MarkupMethod> + 'a {
1039        self.methods.iter().filter(move |m| m.name == name)
1040    }
1041
1042    /// The static field `name` declared by this type.
1043    pub fn find_field(&self, name: &str) -> Option<&'static MarkupField> {
1044        self.fields.iter().find(|f| f.name == name)
1045    }
1046
1047    /// The static property `name` declared by this type.
1048    pub fn find_static_property(&self, name: &str) -> Option<&'static MarkupProperty> {
1049        self.static_properties.iter().find(|p| p.name == name)
1050    }
1051
1052    /// The event `name` declared by this type.
1053    pub fn find_event(&self, name: &str) -> Option<&'static MarkupEvent> {
1054        self.events.iter().find(|e| e.name == name)
1055    }
1056
1057    /// The attribute `name` of the type.
1058    pub fn find_attribute(&self, name: &str) -> Option<&'static MarkupAttribute> {
1059        self.attributes.iter().find(|a| a.name == name)
1060    }
1061
1062    /// The enumeration member `name`; `ignore_case` as the managed
1063    /// `Enum.Parse(.., ignoreCase)`.
1064    pub fn find_enum_member(&self, name: &str, ignore_case: bool) -> Option<&'static MarkupEnumMember> {
1065        self.enum_members
1066            .iter()
1067            .find(|m| if ignore_case { m.name.eq_ignore_ascii_case(name) } else { m.name == name })
1068    }
1069
1070    /// Adds a type to the process-wide table of types with markup metadata,
1071    /// making it discoverable with [`find`](Self::find) and
1072    /// [`find_by_handle`](Self::find_by_handle). Registering a type again
1073    /// does nothing.
1074    ///
1075    /// A crate registers its types from its `register_types()` function.
1076    /// The metadata of a class of the object model needs no registration to
1077    /// be reachable from its runtime type ([`TypeInfo::markup`]).
1078    pub fn register(type_: &'static MarkupType) {
1079        Self::register_all(&[type_]);
1080    }
1081
1082    /// Registers several types; see [`register`](Self::register).
1083    pub fn register_all(types: &[&'static MarkupType]) {
1084        let mut registry = registry().write().unwrap_or_else(|e| e.into_inner());
1085        for type_ in types {
1086            registry.insert(type_);
1087        }
1088    }
1089
1090    /// Registers the Rust type `H` as one more handle of the registered type `type_`: a
1091    /// type of another crate whose values are values of `type_` (a wrapper a property
1092    /// holds the value in), next to the [`handles`](Self::handles) the declaration lists.
1093    /// The crate that declares `H` also registers the casts between `H` and the handles
1094    /// of the type ([`ValueTypes::register_cast`]). A handle that is already known keeps
1095    /// its type.
1096    pub fn register_handle<H: 'static>(type_: &'static MarkupType) {
1097        let mut registry = registry().write().unwrap_or_else(|e| e.into_inner());
1098        registry.insert(type_);
1099        registry.by_handle.entry(TypeId::of::<H>()).or_insert(type_);
1100    }
1101
1102    /// Finds a registered type by namespace and name.
1103    pub fn find(namespace: &str, name: &str) -> Option<&'static MarkupType> {
1104        let candidates: Vec<&'static MarkupType> = {
1105            let registry = registry().read().unwrap_or_else(|e| e.into_inner());
1106            registry.by_name.get(name)?.clone()
1107        };
1108        candidates.into_iter().find(|t| t.namespace() == namespace)
1109    }
1110
1111    /// Finds the registered type that values of the Rust type `handle` are
1112    /// values of (any of its [`handles`](Self::handles)).
1113    pub fn find_by_handle(handle: TypeId) -> Option<&'static MarkupType> {
1114        let registry = registry().read().unwrap_or_else(|e| e.into_inner());
1115        registry.by_handle.get(&handle).copied()
1116    }
1117
1118    /// Finds the registered value type or enumeration whose nullable form
1119    /// ([`nullable`](Self::nullable)) is the Rust type `handle`.
1120    pub fn find_by_nullable_handle(handle: TypeId) -> Option<&'static MarkupType> {
1121        let registry = registry().read().unwrap_or_else(|e| e.into_inner());
1122        registry.by_nullable_handle.get(&handle).copied()
1123    }
1124
1125    /// Finds the registered metadata that belongs to the runtime type
1126    /// `type_` ([`type_info`](Self::type_info)): the metadata of a static
1127    /// type declared with `ferro_markup_type!(static X { type_info: X, .. })`.
1128    /// (The metadata of a class is its [`TypeInfo::markup`].)
1129    pub fn find_by_type_info(type_: &'static TypeInfo) -> Option<&'static MarkupType> {
1130        if let Some(markup) = type_.markup() {
1131            return Some(markup);
1132        }
1133        let registry = registry().read().unwrap_or_else(|e| e.into_inner());
1134        registry.by_type_info.get(&(type_ as *const TypeInfo as usize)).copied()
1135    }
1136
1137    /// Every registered type, in registration order.
1138    pub fn registered_types() -> Vec<&'static MarkupType> {
1139        registry().read().unwrap_or_else(|e| e.into_inner()).types.clone()
1140    }
1141}
1142
1143impl fmt::Debug for MarkupType {
1144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1145        f.write_str(self.name)
1146    }
1147}
1148
1149/// A Rust type that declares markup metadata with
1150/// [`ferro_markup_type!`](crate::ferro_markup_type) or
1151/// [`ferro_markup_enum!`](crate::ferro_markup_enum).
1152pub trait MarkupTyped {
1153    /// The markup metadata of the type.
1154    const MARKUP: &'static MarkupType;
1155}
1156
1157#[derive(Default)]
1158struct MarkupRegistry {
1159    types: Vec<&'static MarkupType>,
1160    by_name: HashMap<&'static str, Vec<&'static MarkupType>>,
1161    by_handle: HashMap<TypeId, &'static MarkupType>,
1162    by_nullable_handle: HashMap<TypeId, &'static MarkupType>,
1163    by_type_info: HashMap<usize, &'static MarkupType>,
1164}
1165
1166impl MarkupRegistry {
1167    fn insert(&mut self, type_: &'static MarkupType) {
1168        let known = self.by_name.get(type_.name).is_some_and(|types| types.iter().any(|t| std::ptr::eq(*t, type_)));
1169        if known {
1170            return;
1171        }
1172        self.types.push(type_);
1173        self.by_name.entry(type_.name).or_default().push(type_);
1174        for handle in type_.handles {
1175            self.by_handle.entry(handle().id()).or_insert(type_);
1176        }
1177        if let Some(type_info) = type_.type_info {
1178            self.by_type_info.entry(type_info() as *const TypeInfo as usize).or_insert(type_);
1179        }
1180        if let Some(nullable) = type_.nullable {
1181            self.by_nullable_handle.entry(nullable().id()).or_insert(type_);
1182        }
1183    }
1184}
1185
1186#[cfg(feature = "compiler-metadata")]
1187fn rust_paths() -> &'static RwLock<HashMap<usize, (&'static MarkupType, &'static str, bool)>> {
1188    static PATHS: OnceLock<RwLock<HashMap<usize, (&'static MarkupType, &'static str, bool)>>> = OnceLock::new();
1189    PATHS.get_or_init(Default::default)
1190}
1191
1192fn registry() -> &'static RwLock<MarkupRegistry> {
1193    static REGISTRY: OnceLock<RwLock<MarkupRegistry>> = OnceLock::new();
1194    REGISTRY.get_or_init(Default::default)
1195}
1196
1197/// The names of the attributes the framework understands, for
1198/// [`MarkupAttribute::name`]. Each is the name of the attribute class of the
1199/// managed original without its `Attribute` suffix; arguments are the ones
1200/// of that class.
1201pub mod attributes {
1202    /// On a property: its value is content built on demand
1203    /// (`TemplateResultType` names the type of the built object).
1204    pub const TEMPLATE_CONTENT: &str = "TemplateContent";
1205    /// On a property: must be assigned after the named property.
1206    pub const DEPENDS_ON: &str = "DependsOn";
1207    /// On a property: a binding assigned in markup is stored as the value
1208    /// instead of being applied.
1209    pub const ASSIGN_BINDING: &str = "AssignBinding";
1210    /// On a type: instances are attached to their parent before their own
1211    /// properties are set.
1212    pub const USABLE_DURING_INITIALIZATION: &str = "UsableDuringInitialization";
1213    /// On a collection type: whitespace between its items is significant.
1214    pub const WHITESPACE_SIGNIFICANT_COLLECTION: &str = "WhitespaceSignificantCollection";
1215    /// On a type: whitespace around an element of the type is trimmed.
1216    pub const TRIM_SURROUNDING_WHITESPACE: &str = "TrimSurroundingWhitespace";
1217    /// On a type or property: names the converter type used to convert
1218    /// text.
1219    pub const TYPE_CONVERTER: &str = "TypeConverter";
1220    /// On a type: it is the scope of a control template.
1221    pub const CONTROL_TEMPLATE_SCOPE: &str = "ControlTemplateScope";
1222    /// On a property: it states the data type of the bindings in scope.
1223    pub const DATA_TYPE: &str = "DataType";
1224    /// On a property: the data type of its bindings is inherited from
1225    /// another property.
1226    pub const INHERIT_DATA_TYPE_FROM: &str = "InheritDataTypeFrom";
1227    /// On a property: the data type of its bindings is the item type of a
1228    /// collection property.
1229    pub const INHERIT_DATA_TYPE_FROM_ITEMS: &str = "InheritDataTypeFromItems";
1230    /// On a property: text assigned in markup is a name to resolve in the
1231    /// name scope.
1232    pub const RESOLVE_BY_NAME: &str = "ResolveByName";
1233    /// On a property of an option markup extension: the option it holds.
1234    pub const MARKUP_EXTENSION_OPTION: &str = "MarkupExtensionOption";
1235    /// On a property of an option markup extension: the default option.
1236    pub const MARKUP_EXTENSION_DEFAULT_OPTION: &str = "MarkupExtensionDefaultOption";
1237    /// On a type or property: how a list is parsed from text (separators,
1238    /// split options).
1239    pub const FERRO_LIST: &str = "FerroList";
1240    /// On a control type: a named part its template must contain.
1241    pub const TEMPLATE_PART: &str = "TemplatePart";
1242    /// On a control type: the pseudo-classes it sets.
1243    pub const PSEUDO_CLASSES: &str = "PseudoClasses";
1244    /// On any member: it is obsolete (the argument is the message).
1245    pub const OBSOLETE: &str = "Obsolete";
1246    /// On any member: it is not stable API.
1247    pub const UNSTABLE: &str = "Unstable";
1248    /// On a type: it is private API.
1249    pub const PRIVATE_API: &str = "PrivateApi";
1250    /// On a type: implemented only by the framework.
1251    pub const NOT_CLIENT_IMPLEMENTABLE: &str = "NotClientImplementable";
1252    /// On a property: the content property (stated with `content:` in the
1253    /// declaration forms; listed for completeness).
1254    pub const CONTENT: &str = "Content";
1255    /// On a constructor parameter or property of a markup extension.
1256    pub const CONSTRUCTOR_ARGUMENT: &str = "ConstructorArgument";
1257}