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