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}