Skip to main content

ferroui_build_scan/
model.rs

1//! The build-time type model of a crate and its serialised form, the
2//! `.xamlmeta` file (docs/porting/xaml.md, 9.5.1).
3//!
4//! The model states what the declarations of a crate say about its types:
5//! the classes and their registered properties, the markup metadata of
6//! classes and of other types, the enumerations, the namespaces and the
7//! assembly. It is filled by the source scanner ([`crate::scanner`]) or read
8//! from the `.xamlmeta` of a crate whose sources are not read. Everything in
9//! it is owned text; a type is referred to by its Rust type text
10//! ([`RustType`]), never by a handle of the process.
11//!
12//! # The file
13//!
14//! JSON ([`crate::json`]). Two formats exist, told apart by the member
15//! `"format"`:
16//!
17//! - no `"format"` member: format 1, the file the emitter writes today
18//!   (`rust_emitter::XamlMetadata`): `name`, `crate_name`, `documents`,
19//!   `dependencies`. [`AssemblyModel::parse`] reads it as a model without
20//!   types.
21//! - `"format": 2`: the four members of format 1, unchanged, and the type
22//!   model next to them. The reader of format 1 looks its members up by name
23//!   and ignores the others, so it reads a file of format 2 as the documents
24//!   of the crate.
25//!
26//! A member whose value is the default of its kind (an empty list, `false`,
27//! nothing) is left out of a file of format 2, except the four members of
28//! format 1, which are always written.
29
30use crate::json::{text_of, Fields, Json, Members};
31pub use crate::xaml_metadata::{DocumentModel, XamlMetadata};
32
33/// The format [`AssemblyModel::to_json`] writes.
34pub const FORMAT: i64 = 2;
35
36/// A Rust type as normalised text (9.5.2, step 1): the tokens of the type as the
37/// declaration writes it, spaced canonically, with every path the scanner resolved made
38/// absolute (`::ferroui_base::Ref<::ferroui_controls::control::Control>`). A path of the
39/// scanned crate names the module that declares the item; a path into another crate is
40/// absolute as the `use` item of the file spells it. Names of the language prelude and
41/// the primitive types stay as they are (`Option`, `String`, `f64`).
42#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
43pub struct RustType {
44    /// The normalised text.
45    pub text: String,
46    /// The paths of the text the scanner could not resolve, as written, each once, in
47    /// the order of the text. They are kept in the text as written.
48    pub unresolved: Vec<String>,
49}
50
51impl RustType {
52    /// A type whose every path is resolved.
53    pub fn resolved(text: &str) -> Self {
54        Self { text: text.to_string(), unresolved: Vec::new() }
55    }
56
57    /// Whether every path of the text is resolved.
58    pub fn is_resolved(&self) -> bool {
59        self.unresolved.is_empty()
60    }
61
62    fn to_json(&self) -> Json {
63        if self.unresolved.is_empty() {
64            Json::string(&self.text)
65        } else {
66            Json::object(Members::new().text("text", &self.text).list("unresolved", &self.unresolved, |path| Json::string(path)))
67        }
68    }
69
70    fn from_json(value: &Json) -> Result<Self, String> {
71        match value {
72            Json::String(text) => Ok(Self::resolved(text)),
73            _ => {
74                let fields = Fields::of(value, "a type")?;
75                Ok(Self { text: fields.text("text")?, unresolved: fields.list("unresolved", |path| text_of(path, "an unresolved path"))? })
76            }
77        }
78    }
79}
80
81/// The kind of a type, as the declaration states it.
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum TypeKind {
84    /// A class of the object model (`ferro_class!`) or a plain class (`ferro_markup_type!(class ..)`).
85    Class,
86    Struct,
87    Enum,
88    Interface,
89    /// A type without instances: the owner of attached properties or of static members.
90    Static,
91}
92
93impl TypeKind {
94    fn name(self) -> &'static str {
95        match self {
96            TypeKind::Class => "class",
97            TypeKind::Struct => "struct",
98            TypeKind::Enum => "enum",
99            TypeKind::Interface => "interface",
100            TypeKind::Static => "static",
101        }
102    }
103
104    fn of(name: &str) -> Result<Self, String> {
105        match name {
106            "class" => Ok(TypeKind::Class),
107            "struct" => Ok(TypeKind::Struct),
108            "enum" => Ok(TypeKind::Enum),
109            "interface" => Ok(TypeKind::Interface),
110            "static" => Ok(TypeKind::Static),
111            other => Err(format!("\"{other}\" is not a kind of type")),
112        }
113    }
114}
115
116/// A callable of a declaration: a path (`Setter::set_value`) or any other expression (a
117/// closure).
118#[derive(Clone, Debug, Default, PartialEq, Eq)]
119pub struct CallableModel {
120    /// The path as written, when the callable is a path; nothing for an expression.
121    pub path: Option<String>,
122    /// The path made absolute, when the scanner resolved its head.
123    pub resolved: Option<String>,
124    /// For a closure without parameters whose body dereferences the result of a function
125    /// called without arguments (`|| *Button::click_event()`, the form a static field of
126    /// a routed event is declared in): the path of that function, made absolute.
127    pub dereferenced: Option<String>,
128}
129
130impl CallableModel {
131    fn to_json(&self) -> Json {
132        Json::object(
133            Members::new().optional_text("path", &self.path).optional_text("resolved", &self.resolved).optional_text("dereferenced", &self.dereferenced),
134        )
135    }
136
137    fn from_json(value: &Json) -> Result<Self, String> {
138        let fields = Fields::of(value, "a callable")?;
139        Ok(Self { path: fields.optional_text("path")?, resolved: fields.optional_text("resolved")?, dereferenced: fields.optional_text("dereferenced")? })
140    }
141}
142
143/// How generated code calls a member (9.5.3). The scan chooses it for every callable of
144/// the model once the models of the dependencies are attached ([`crate::call_forms`]).
145#[derive(Clone, Debug, PartialEq, Eq)]
146pub enum CallForm {
147    /// Form A: typed code derived from the declaration. The callable is a function a
148    /// declaration macro writes for the type (the accessor of a routed event or of a
149    /// registered property), or the closure that dereferences what such a function
150    /// returns ([`CallableModel::dereferenced`]); generated code calls the function by
151    /// the public path of the type.
152    Structural,
153    /// Form B: the public function with this absolute path (the public path of the type
154    /// and the name of the function), called with the instance by reference and the
155    /// arguments in the declared types.
156    Path(String),
157    /// Form B for code generated into the crate of the declaration only: the function is
158    /// visible in its crate (`pub(crate)`). For any other crate the member is called as
159    /// [`Invoker`](Self::Invoker).
160    CratePath(String),
161    /// Form C: the invoker of the metadata.
162    Invoker,
163}
164
165impl CallForm {
166    /// The form as a crate other than the declaring one calls the member.
167    pub fn from_another_crate(&self) -> &CallForm {
168        match self {
169            CallForm::CratePath(_) => &CallForm::Invoker,
170            other => other,
171        }
172    }
173
174    fn to_json(&self) -> Json {
175        match self {
176            CallForm::Structural => Json::string("structural"),
177            CallForm::Invoker => Json::string("invoker"),
178            CallForm::Path(path) => Json::object(Members::new().text("path", path)),
179            CallForm::CratePath(path) => Json::object(Members::new().text("path", path).flag("crate", true)),
180        }
181    }
182
183    fn from_json(value: &Json) -> Result<Self, String> {
184        match value {
185            Json::String(text) if text == "structural" => Ok(CallForm::Structural),
186            Json::String(text) if text == "invoker" => Ok(CallForm::Invoker),
187            Json::String(text) => Err(format!("\"{text}\" is not a call form")),
188            _ => {
189                let fields = Fields::of(value, "a call form")?;
190                let path = fields.text("path")?;
191                Ok(if fields.flag("crate")? { CallForm::CratePath(path) } else { CallForm::Path(path) })
192            }
193        }
194    }
195}
196
197/// An argument of an attribute.
198#[derive(Clone, Debug, PartialEq, Eq)]
199pub enum AttributeValueModel {
200    Null,
201    Bool(bool),
202    /// An integer or a character (its code point, as the metadata holds it).
203    Int(i64),
204    /// A floating point literal, as written.
205    Float(String),
206    Str(String),
207    /// `type(T)`.
208    Type(RustType),
209    Array(Vec<AttributeValueModel>),
210}
211
212impl AttributeValueModel {
213    fn to_json(&self) -> Json {
214        match self {
215            AttributeValueModel::Null => Json::Null,
216            AttributeValueModel::Bool(value) => Json::Bool(*value),
217            AttributeValueModel::Int(value) => Json::Integer(*value),
218            AttributeValueModel::Float(text) => Json::Object(vec![("float".to_string(), Json::string(text))]),
219            AttributeValueModel::Str(text) => Json::string(text),
220            AttributeValueModel::Type(type_) => Json::Object(vec![("type".to_string(), type_.to_json())]),
221            AttributeValueModel::Array(items) => Json::Array(items.iter().map(Self::to_json).collect()),
222        }
223    }
224
225    fn from_json(value: &Json) -> Result<Self, String> {
226        match value {
227            Json::Null => Ok(AttributeValueModel::Null),
228            Json::Bool(value) => Ok(AttributeValueModel::Bool(*value)),
229            Json::Integer(value) => Ok(AttributeValueModel::Int(*value)),
230            Json::Float(_) => Err("a floating point argument of an attribute is written as {\"float\": \"text\"}".to_string()),
231            Json::String(text) => Ok(AttributeValueModel::Str(text.clone())),
232            Json::Array(items) => Ok(AttributeValueModel::Array(items.iter().map(Self::from_json).collect::<Result<_, _>>()?)),
233            Json::Object(_) => {
234                let fields = Fields::of(value, "an argument of an attribute")?;
235                if let Some(text) = fields.optional_text("float")? {
236                    return Ok(AttributeValueModel::Float(text));
237                }
238                match fields.get("type") {
239                    Some(type_) => Ok(AttributeValueModel::Type(RustType::from_json(type_)?)),
240                    None => Err("an argument of an attribute is an object without \"float\" and \"type\"".to_string()),
241                }
242            }
243        }
244    }
245}
246
247/// An attribute of a type, a member or a parameter (`DependsOn("Property")`).
248#[derive(Clone, Debug, Default, PartialEq, Eq)]
249pub struct AttributeModel {
250    /// The name, without the `Attribute` suffix.
251    pub name: String,
252    /// The positional arguments.
253    pub arguments: Vec<AttributeValueModel>,
254    /// The named arguments (`IsRequired = true`).
255    pub properties: Vec<(String, AttributeValueModel)>,
256}
257
258impl AttributeModel {
259    fn to_json(&self) -> Json {
260        Json::object(
261            Members::new()
262                .text("name", &self.name)
263                .list("arguments", &self.arguments, AttributeValueModel::to_json)
264                .list("properties", &self.properties, |(name, value)| Json::Array(vec![Json::string(name), value.to_json()])),
265        )
266    }
267
268    fn from_json(value: &Json) -> Result<Self, String> {
269        let fields = Fields::of(value, "an attribute")?;
270        Ok(Self {
271            name: fields.text("name")?,
272            arguments: fields.list("arguments", AttributeValueModel::from_json)?,
273            properties: fields.list("properties", |pair| match pair {
274                Json::Array(pair) if pair.len() == 2 => Ok((text_of(&pair[0], "the name of a named argument")?, AttributeValueModel::from_json(&pair[1])?)),
275                _ => Err("a named argument of an attribute is not a pair".to_string()),
276            })?,
277        })
278    }
279}
280
281fn attributes_to_json(members: Members, attributes: &[AttributeModel]) -> Members {
282    members.list("attributes", attributes, AttributeModel::to_json)
283}
284
285/// A parameter of a constructor, a method, an indexer or an event handler.
286#[derive(Clone, Debug, Default, PartialEq, Eq)]
287pub struct ParameterModel {
288    /// The name, for a constructor that names its parameters.
289    pub name: Option<String>,
290    pub type_: RustType,
291    pub attributes: Vec<AttributeModel>,
292}
293
294impl ParameterModel {
295    fn to_json(&self) -> Json {
296        if self.name.is_none() && self.attributes.is_empty() {
297            return self.type_.to_json();
298        }
299        Json::object(attributes_to_json(Members::new().optional_text("name", &self.name).always("type", self.type_.to_json()), &self.attributes))
300    }
301
302    fn from_json(value: &Json) -> Result<Self, String> {
303        if let Json::Object(members) = value {
304            if members.iter().any(|(name, _)| name == "type") {
305                let fields = Fields::of(value, "a parameter")?;
306                let type_ = fields.get("type").ok_or_else(|| "\"type\" of a parameter is null".to_string())?;
307                return Ok(Self {
308                    name: fields.optional_text("name")?,
309                    type_: RustType::from_json(type_)?,
310                    attributes: fields.list("attributes", AttributeModel::from_json)?,
311                });
312            }
313        }
314        Ok(Self { name: None, type_: RustType::from_json(value)?, attributes: Vec::new() })
315    }
316}
317
318/// A constructor, a method, a static field or an event of a type (`MemberModel`).
319///
320/// - a constructor: `name` is empty, `return_type` is nothing (the value type of the type);
321/// - a method: `return_type` is nothing for a method without a result;
322/// - a static field: no parameters, `return_type` is the type of the field;
323/// - an event: the parameters are the ones of the handler, the callable subscribes.
324#[derive(Clone, Debug, Default, PartialEq, Eq)]
325pub struct MemberModel {
326    pub name: String,
327    pub parameters: Vec<ParameterModel>,
328    pub return_type: Option<RustType>,
329    pub is_static: bool,
330    /// Declared with `try`: the callable returns a `Result`.
331    pub fallible: bool,
332    pub attributes: Vec<AttributeModel>,
333    /// The callable of the declaration.
334    pub callable: CallableModel,
335    /// The associated function of the declared type the declaration macro writes for the
336    /// member with the feature `markup-functions` of the base crate (`__markup_new_0`,
337    /// `__markup_Add_1`, `__markup_field_ClickEvent`); nothing for an event.
338    pub typed_function: Option<String>,
339    /// The call form (9.5.3), once it is chosen.
340    pub call: Option<CallForm>,
341}
342
343impl MemberModel {
344    fn to_json(&self) -> Json {
345        let members = Members::new()
346            .text_or_empty("name", &self.name)
347            .list("parameters", &self.parameters, ParameterModel::to_json)
348            .optional("return_type", self.return_type.as_ref().map(RustType::to_json))
349            .flag("is_static", self.is_static)
350            .flag("fallible", self.fallible);
351        Json::object(
352            attributes_to_json(members, &self.attributes)
353                .always("callable", self.callable.to_json())
354                .optional_text("typed_function", &self.typed_function)
355                .optional("call", self.call.as_ref().map(CallForm::to_json)),
356        )
357    }
358
359    fn from_json(value: &Json) -> Result<Self, String> {
360        let fields = Fields::of(value, "a member")?;
361        Ok(Self {
362            name: fields.text_or_empty("name")?,
363            parameters: fields.list("parameters", ParameterModel::from_json)?,
364            return_type: fields.optional("return_type", RustType::from_json)?,
365            is_static: fields.flag("is_static")?,
366            fallible: fields.flag("fallible")?,
367            attributes: fields.list("attributes", AttributeModel::from_json)?,
368            callable: fields.optional("callable", CallableModel::from_json)?.unwrap_or_default(),
369            typed_function: fields.optional_text("typed_function")?,
370            call: fields.optional("call", CallForm::from_json)?,
371        })
372    }
373}
374
375/// An accessor of a plain property or of an indexer.
376#[derive(Clone, Debug, Default, PartialEq, Eq)]
377pub struct AccessorModel {
378    /// Declared with `try_get:` / `try_set:`.
379    pub fallible: bool,
380    pub callable: CallableModel,
381    /// The typed function of the accessor (`__markup_get_Name`); nothing for an indexer.
382    pub typed_function: Option<String>,
383    /// The call form (9.5.3), once it is chosen.
384    pub call: Option<CallForm>,
385}
386
387impl AccessorModel {
388    fn to_json(&self) -> Json {
389        Json::object(
390            Members::new()
391                .flag("fallible", self.fallible)
392                .always("callable", self.callable.to_json())
393                .optional_text("typed_function", &self.typed_function)
394                .optional("call", self.call.as_ref().map(CallForm::to_json)),
395        )
396    }
397
398    fn from_json(value: &Json) -> Result<Self, String> {
399        let fields = Fields::of(value, "an accessor")?;
400        Ok(Self {
401            fallible: fields.flag("fallible")?,
402            callable: fields.optional("callable", CallableModel::from_json)?.unwrap_or_default(),
403            typed_function: fields.optional_text("typed_function")?,
404            call: fields.optional("call", CallForm::from_json)?,
405        })
406    }
407}
408
409/// A plain (not registered) property of a type, instance or static, or an indexer
410/// (`name` is empty and the parameters are the ones of the index).
411#[derive(Clone, Debug, Default, PartialEq, Eq)]
412pub struct PropertyModel {
413    pub name: String,
414    /// The parameters of an indexer.
415    pub parameters: Vec<ParameterModel>,
416    pub value_type: RustType,
417    pub getter: Option<AccessorModel>,
418    pub setter: Option<AccessorModel>,
419    pub attributes: Vec<AttributeModel>,
420}
421
422impl PropertyModel {
423    fn to_json(&self) -> Json {
424        let members = Members::new()
425            .text_or_empty("name", &self.name)
426            .list("parameters", &self.parameters, ParameterModel::to_json)
427            .always("value_type", self.value_type.to_json())
428            .optional("getter", self.getter.as_ref().map(AccessorModel::to_json))
429            .optional("setter", self.setter.as_ref().map(AccessorModel::to_json));
430        Json::object(attributes_to_json(members, &self.attributes))
431    }
432
433    fn from_json(value: &Json) -> Result<Self, String> {
434        let fields = Fields::of(value, "a property")?;
435        Ok(Self {
436            name: fields.text_or_empty("name")?,
437            parameters: fields.list("parameters", ParameterModel::from_json)?,
438            value_type: fields.optional("value_type", RustType::from_json)?.ok_or_else(|| "\"value_type\" is missing in a property".to_string())?,
439            getter: fields.optional("getter", AccessorModel::from_json)?,
440            setter: fields.optional("setter", AccessorModel::from_json)?,
441            attributes: fields.list("attributes", AttributeModel::from_json)?,
442        })
443    }
444}
445
446/// The kind of a registered property, from the type of its accessor.
447#[derive(Clone, Copy, Debug, PartialEq, Eq)]
448pub enum RegisteredKind {
449    Styled,
450    Direct,
451    Attached,
452}
453
454impl RegisteredKind {
455    fn name(self) -> &'static str {
456        match self {
457            RegisteredKind::Styled => "styled",
458            RegisteredKind::Direct => "direct",
459            RegisteredKind::Attached => "attached",
460        }
461    }
462
463    fn of(name: &str) -> Result<Self, String> {
464        match name {
465            "styled" => Ok(RegisteredKind::Styled),
466            "direct" => Ok(RegisteredKind::Direct),
467            "attached" => Ok(RegisteredKind::Attached),
468            other => Err(format!("\"{other}\" is not a kind of registered property")),
469        }
470    }
471}
472
473/// What the body of the accessor of a registered property does.
474#[derive(Clone, Copy, Debug, PartialEq, Eq)]
475pub enum RegistrationModel {
476    /// `FerroProperty::register*::<Owner, ..>("Name", ..)`: the property is declared here.
477    Declared,
478    /// `Other::name_property().add_owner*::<Owner>(..)`: a property another type declares,
479    /// with this type as one more owner.
480    AddedOwner,
481    /// The body is the accessor of another property and nothing else: the same property
482    /// under a second accessor.
483    Alias,
484    /// None of the forms: the scanner could not tell (a diagnostic says where).
485    Unknown,
486}
487
488impl RegistrationModel {
489    fn name(self) -> &'static str {
490        match self {
491            RegistrationModel::Declared => "declared",
492            RegistrationModel::AddedOwner => "added_owner",
493            RegistrationModel::Alias => "alias",
494            RegistrationModel::Unknown => "unknown",
495        }
496    }
497
498    fn of(name: &str) -> Result<Self, String> {
499        match name {
500            "declared" => Ok(RegistrationModel::Declared),
501            "added_owner" => Ok(RegistrationModel::AddedOwner),
502            "alias" => Ok(RegistrationModel::Alias),
503            "unknown" => Ok(RegistrationModel::Unknown),
504            other => Err(format!("\"{other}\" is not a registration")),
505        }
506    }
507}
508
509/// The accessor of a registered property of a type (`RegisteredModel`): one function of
510/// its `ferro_properties!` block.
511#[derive(Clone, Debug, PartialEq, Eq)]
512pub struct RegisteredModel {
513    /// The name of the property (`Background`): the text of the registration, or, for an
514    /// added owner and an alias, the name of the property the source accessor declares
515    /// when that accessor is in the scanned crate. Nothing when the source is in another
516    /// crate (its model has the name) or the registration was not read.
517    pub name: Option<String>,
518    pub kind: RegisteredKind,
519    /// The value type: the last type argument of the type of the accessor.
520    pub value_type: RustType,
521    /// The owner the registration names: `register::<Owner, _>` of a declared property,
522    /// `add_owner::<Owner>` of an added owner. The registry lists the property under this
523    /// type, whichever type has the accessor.
524    pub owner: Option<RustType>,
525    /// The host type of an attached property (`register_attached::<Owner, Host, _>`).
526    pub host: Option<RustType>,
527    /// The name of the accessor function (`background_property`); its path is the path of
528    /// the type and the name, unless the accessor is a function of another type
529    /// ([`function_of`](Self::function_of)).
530    pub accessor: String,
531    /// The Rust path of the type whose function the accessor is, when it is not the type
532    /// the property is listed under: `ferro_property!(for Owner; ..)` among the members of
533    /// `impl Other` declares `Other::name_property()`, a property of `Owner`
534    /// (`::ferroui_base::styling::theme_variant::ThemeVariant` for the theme variant
535    /// properties of `StyledElement`).
536    pub function_of: Option<String>,
537    /// The visibility of the accessor, as written (`pub`, `pub(crate)`; empty when private).
538    pub visibility: String,
539    pub registration: RegistrationModel,
540    /// The accessor the body calls, for an added owner and an alias, as written
541    /// (`Decorator::child_property`).
542    pub source: Option<CallableModel>,
543    /// The body states `.assign_binding(true)` (or `set_assign_binding(true)`).
544    pub assign_binding: bool,
545    /// The body states `.inherits(true)`.
546    pub inherits: bool,
547    /// The registration of a direct property states no setter (`None` where the setter
548    /// goes): the property is read-only.
549    pub read_only: bool,
550    /// The types of the scanned crate that add themselves as owners of the property, by
551    /// their Rust path, in the order of the scan. Owners in other crates are in the models
552    /// of those crates.
553    pub added_owners: Vec<String>,
554}
555
556impl RegisteredModel {
557    fn to_json(&self) -> Json {
558        Json::object(
559            Members::new()
560                .optional_text("name", &self.name)
561                .text("kind", self.kind.name())
562                .always("value_type", self.value_type.to_json())
563                .optional("owner", self.owner.as_ref().map(RustType::to_json))
564                .optional("host", self.host.as_ref().map(RustType::to_json))
565                .text("accessor", &self.accessor)
566                .optional_text("function_of", &self.function_of)
567                .text_or_empty("visibility", &self.visibility)
568                .text("registration", self.registration.name())
569                .optional("source", self.source.as_ref().map(CallableModel::to_json))
570                .flag("assign_binding", self.assign_binding)
571                .flag("inherits", self.inherits)
572                .flag("read_only", self.read_only)
573                .list("added_owners", &self.added_owners, |owner| Json::string(owner)),
574        )
575    }
576
577    fn from_json(value: &Json) -> Result<Self, String> {
578        let fields = Fields::of(value, "a registered property")?;
579        Ok(Self {
580            name: fields.optional_text("name")?,
581            kind: RegisteredKind::of(&fields.text("kind")?)?,
582            value_type: fields.optional("value_type", RustType::from_json)?.ok_or_else(|| "\"value_type\" is missing in a registered property".to_string())?,
583            owner: fields.optional("owner", RustType::from_json)?,
584            host: fields.optional("host", RustType::from_json)?,
585            accessor: fields.text("accessor")?,
586            function_of: fields.optional_text("function_of")?,
587            visibility: fields.text_or_empty("visibility")?,
588            registration: RegistrationModel::of(&fields.text("registration")?)?,
589            source: fields.optional("source", CallableModel::from_json)?,
590            assign_binding: fields.flag("assign_binding")?,
591            inherits: fields.flag("inherits")?,
592            read_only: fields.flag("read_only")?,
593            added_owners: fields.list("added_owners", |owner| text_of(owner, "an added owner"))?,
594        })
595    }
596}
597
598/// A member of an enumeration.
599#[derive(Clone, Debug, Default, PartialEq, Eq)]
600pub struct EnumMemberModel {
601    /// The markup name.
602    pub name: String,
603    /// The Rust variant of a plain enumeration (`Self_` for the member `Self`); nothing
604    /// for a member of a set of flags.
605    pub rust_variant: Option<String>,
606    /// The constant of a member of a set of flags, as written (`RoutingStrategies::DIRECT`).
607    pub rust_value: Option<String>,
608    /// The numeric value, when the scanner found the declaration of the enumeration (or
609    /// of the flags) in the crate and could evaluate the member.
610    pub value: Option<i64>,
611}
612
613impl EnumMemberModel {
614    fn to_json(&self) -> Json {
615        Json::object(
616            Members::new()
617                .text("name", &self.name)
618                .optional_text("rust_variant", &self.rust_variant)
619                .optional_text("rust_value", &self.rust_value)
620                .optional("value", self.value.map(Json::Integer)),
621        )
622    }
623
624    fn from_json(value: &Json) -> Result<Self, String> {
625        let fields = Fields::of(value, "a member of an enumeration")?;
626        Ok(Self {
627            name: fields.text("name")?,
628            rust_variant: fields.optional_text("rust_variant")?,
629            rust_value: fields.optional_text("rust_value")?,
630            value: fields.optional_integer("value")?,
631        })
632    }
633}
634
635/// `generic: "FerroList`1" [T]`: the generic definition a type instantiates.
636#[derive(Clone, Debug, Default, PartialEq, Eq)]
637pub struct GenericModel {
638    pub definition: String,
639    pub arguments: Vec<RustType>,
640}
641
642/// A type of a crate (`TypeModel`).
643#[derive(Clone, Debug, PartialEq, Eq)]
644pub struct TypeModel {
645    /// The dotted namespace: the explicit one, else the one the namespace table of the
646    /// crate gives the declaring module; empty when the table has none.
647    pub namespace: String,
648    /// `namespace: ".."` of the markup metadata.
649    pub explicit_namespace: Option<String>,
650    /// The markup name.
651    pub name: String,
652    pub kind: TypeKind,
653    /// The type is a type of the object model: a class (`ferro_class!`) or the static
654    /// owner type of attached properties (`ferro_static_type!`), with a runtime type of
655    /// its own. Its handles (`Ref<X>`, `Option<Ref<X>>` for a class) are implied.
656    pub object_model: bool,
657    /// The class states markup metadata of its own (`markup: { .. }` of a
658    /// `ferro_class_info!`): its runtime type has metadata, whatever the part lists.
659    pub class_markup: bool,
660    /// The crate has a list of the classes it registers (`const TYPES: &[&TypeInfo]`, which
661    /// its `register_types()` hands to `TypeInfo::register_all`) and the type is not in
662    /// it: the type is not known by its name or by its handle until an instance of it is
663    /// created. Never set for a crate without such a list, and never for a type that is
664    /// not of the object model.
665    pub unregistered: bool,
666    /// The Rust type: for a type the crate declares, the absolute path of the declaring
667    /// module and the name; for a type the metadata is declared for, the type as the
668    /// declaration writes it, normalised.
669    pub rust_path: RustType,
670    /// The shortest path another crate names the type by, when the scanner found one
671    /// (`::ferroui_controls::Border`).
672    pub public_path: Option<String>,
673    /// The path the crate states for the type where no public path is found: the one of an
674    /// instantiation of a generic type (`generics:` of `ferro_rust_paths!`), written with
675    /// the name the crate has for the generic type and the public paths of its arguments
676    /// (`::ferroui_controls::FerroListOf<::ferroui_base::Ref<::ferroui_controls::RowDefinition>>`).
677    pub stated_path: Option<String>,
678    /// The module of the declaration that gives the type its namespace
679    /// (`ferroui_controls::border`).
680    pub module: String,
681    /// The `cfg` conditions the declaration is under, as written, outermost first.
682    pub cfg: Vec<String>,
683    /// The Rust types that hold a value of the type, the untyped form first. For a class
684    /// of the object model `Ref<X>` and `Option<Ref<X>>` are implied and not listed.
685    pub handles: Vec<RustType>,
686    /// The type instance members receive (`this:`).
687    pub this: Option<RustType>,
688    /// The base class of a class of the object model, or the base type by handle (`base:`).
689    pub base: Option<RustType>,
690    /// The contracts, by handle (`interfaces:`).
691    pub interfaces: Vec<RustType>,
692    pub generic: Option<GenericModel>,
693    /// `type_info: X`: the runtime type the metadata of a static type belongs to.
694    pub type_info: Option<RustType>,
695    /// `new:` of a class: the constructor without parameters.
696    pub default_constructor: Option<CallableModel>,
697    pub content_property: Option<String>,
698    /// `parse:` (`has_parse` of the design is `parse.is_some()`); its typed function is
699    /// `__markup_parse`.
700    pub parse: Option<CallableModel>,
701    /// The call form of `parse:` (9.5.3), once it is chosen.
702    pub parse_call: Option<CallForm>,
703    pub constructors: Vec<MemberModel>,
704    pub properties: Vec<PropertyModel>,
705    pub static_properties: Vec<PropertyModel>,
706    pub indexers: Vec<PropertyModel>,
707    pub registered: Vec<RegisteredModel>,
708    pub methods: Vec<MemberModel>,
709    pub fields: Vec<MemberModel>,
710    pub events: Vec<MemberModel>,
711    pub enum_members: Vec<EnumMemberModel>,
712    pub is_flags: bool,
713    pub attributes: Vec<AttributeModel>,
714    /// The attributes of registered properties, by the name of the property.
715    pub property_attributes: Vec<(String, Vec<AttributeModel>)>,
716    /// `notify_property_changed: T`.
717    pub notify_property_changed: Option<RustType>,
718}
719
720impl TypeModel {
721    /// A type with nothing but its identity.
722    pub fn new(name: &str, kind: TypeKind, rust_path: RustType, module: &str) -> Self {
723        Self {
724            namespace: String::new(),
725            explicit_namespace: None,
726            name: name.to_string(),
727            kind,
728            object_model: false,
729            class_markup: false,
730            unregistered: false,
731            rust_path,
732            public_path: None,
733            stated_path: None,
734            module: module.to_string(),
735            cfg: Vec::new(),
736            handles: Vec::new(),
737            this: None,
738            base: None,
739            interfaces: Vec::new(),
740            generic: None,
741            type_info: None,
742            default_constructor: None,
743            content_property: None,
744            parse: None,
745            parse_call: None,
746            constructors: Vec::new(),
747            properties: Vec::new(),
748            static_properties: Vec::new(),
749            indexers: Vec::new(),
750            registered: Vec::new(),
751            methods: Vec::new(),
752            fields: Vec::new(),
753            events: Vec::new(),
754            enum_members: Vec::new(),
755            is_flags: false,
756            attributes: Vec::new(),
757            property_attributes: Vec::new(),
758            notify_property_changed: None,
759        }
760    }
761
762    /// The namespace-qualified name (`FerroUI.Controls.Border`).
763    pub fn full_name(&self) -> String {
764        if self.namespace.is_empty() {
765            self.name.clone()
766        } else {
767            format!("{}.{}", self.namespace, self.name)
768        }
769    }
770
771    /// The registered property of the type with the name `name`.
772    pub fn registered(&self, name: &str) -> Option<&RegisteredModel> {
773        self.registered.iter().find(|registered| registered.name.as_deref() == Some(name))
774    }
775
776    /// The path generated code and other crates name the type by: the public path when
777    /// the scanner found one, else the Rust type.
778    pub fn named_path(&self) -> &str {
779        self.public_path.as_deref().unwrap_or(&self.rust_path.text)
780    }
781
782    /// Calls `visit` with every type text of the type: its own, its handles, base and
783    /// interfaces, and the types of its members and of the arguments of its attributes.
784    pub fn visit_types_mut(&mut self, visit: &mut dyn FnMut(&mut RustType)) {
785        fn value(value_: &mut AttributeValueModel, visit: &mut dyn FnMut(&mut RustType)) {
786            match value_ {
787                AttributeValueModel::Type(type_) => visit(type_),
788                AttributeValueModel::Array(items) => {
789                    for item in items {
790                        value(item, visit);
791                    }
792                }
793                _ => {}
794            }
795        }
796        fn attributes(attributes_: &mut [AttributeModel], visit: &mut dyn FnMut(&mut RustType)) {
797            for attribute in attributes_ {
798                for argument in &mut attribute.arguments {
799                    value(argument, visit);
800                }
801                for (_, argument) in &mut attribute.properties {
802                    value(argument, visit);
803                }
804            }
805        }
806        fn parameters(parameters_: &mut [ParameterModel], visit: &mut dyn FnMut(&mut RustType)) {
807            for parameter in parameters_ {
808                visit(&mut parameter.type_);
809                attributes(&mut parameter.attributes, visit);
810            }
811        }
812        fn members(members_: &mut [MemberModel], visit: &mut dyn FnMut(&mut RustType)) {
813            for member in members_ {
814                parameters(&mut member.parameters, visit);
815                if let Some(type_) = &mut member.return_type {
816                    visit(type_);
817                }
818                attributes(&mut member.attributes, visit);
819            }
820        }
821        fn properties(properties_: &mut [PropertyModel], visit: &mut dyn FnMut(&mut RustType)) {
822            for property in properties_ {
823                parameters(&mut property.parameters, visit);
824                visit(&mut property.value_type);
825                attributes(&mut property.attributes, visit);
826            }
827        }
828        visit(&mut self.rust_path);
829        for type_ in self.handles.iter_mut().chain(&mut self.interfaces) {
830            visit(type_);
831        }
832        for type_ in [&mut self.this, &mut self.base, &mut self.type_info, &mut self.notify_property_changed].into_iter().flatten() {
833            visit(type_);
834        }
835        if let Some(generic) = &mut self.generic {
836            for type_ in &mut generic.arguments {
837                visit(type_);
838            }
839        }
840        members(&mut self.constructors, visit);
841        members(&mut self.methods, visit);
842        members(&mut self.fields, visit);
843        members(&mut self.events, visit);
844        properties(&mut self.properties, visit);
845        properties(&mut self.static_properties, visit);
846        properties(&mut self.indexers, visit);
847        for registered in &mut self.registered {
848            visit(&mut registered.value_type);
849            for type_ in registered.owner.iter_mut().chain(registered.host.iter_mut()) {
850                visit(type_);
851            }
852        }
853        attributes(&mut self.attributes, visit);
854        for (_, property_attributes) in &mut self.property_attributes {
855            attributes(property_attributes, visit);
856        }
857    }
858
859    /// Calls `visit` with every callable of the type that is a path: its constructors,
860    /// `parse:`, the accessors of its properties, its members, and the sources of its
861    /// registered properties.
862    pub fn visit_callables_mut(&mut self, visit: &mut dyn FnMut(&mut CallableModel)) {
863        for callable in self.default_constructor.iter_mut().chain(self.parse.iter_mut()) {
864            visit(callable);
865        }
866        for member in self.constructors.iter_mut().chain(&mut self.methods).chain(&mut self.fields).chain(&mut self.events) {
867            visit(&mut member.callable);
868        }
869        for property in self.properties.iter_mut().chain(&mut self.static_properties).chain(&mut self.indexers) {
870            for accessor in property.getter.iter_mut().chain(property.setter.iter_mut()) {
871                visit(&mut accessor.callable);
872            }
873        }
874        for registered in &mut self.registered {
875            if let Some(callable) = &mut registered.source {
876                visit(callable);
877            }
878        }
879    }
880
881    fn to_json(&self) -> Json {
882        let optional_type = |type_: &Option<RustType>| type_.as_ref().map(RustType::to_json);
883        let members = Members::new()
884            .text_or_empty("namespace", &self.namespace)
885            .optional_text("explicit_namespace", &self.explicit_namespace)
886            .text("name", &self.name)
887            .text("kind", self.kind.name())
888            .flag("object_model", self.object_model)
889            .flag("class_markup", self.class_markup)
890            .flag("unregistered", self.unregistered)
891            .always("rust_path", self.rust_path.to_json())
892            .optional_text("public_path", &self.public_path)
893            .optional_text("stated_path", &self.stated_path)
894            .text_or_empty("module", &self.module)
895            .list("cfg", &self.cfg, |condition| Json::string(condition))
896            .list("handles", &self.handles, RustType::to_json)
897            .optional("this", optional_type(&self.this))
898            .optional("base", optional_type(&self.base))
899            .list("interfaces", &self.interfaces, RustType::to_json)
900            .optional(
901                "generic",
902                self.generic.as_ref().map(|generic| {
903                    Json::object(Members::new().text("definition", &generic.definition).list("arguments", &generic.arguments, RustType::to_json))
904                }),
905            )
906            .optional("type_info", optional_type(&self.type_info))
907            .optional("default_constructor", self.default_constructor.as_ref().map(CallableModel::to_json))
908            .optional_text("content_property", &self.content_property)
909            .optional("parse", self.parse.as_ref().map(CallableModel::to_json))
910            .optional("parse_call", self.parse_call.as_ref().map(CallForm::to_json))
911            .list("constructors", &self.constructors, MemberModel::to_json)
912            .list("properties", &self.properties, PropertyModel::to_json)
913            .list("static_properties", &self.static_properties, PropertyModel::to_json)
914            .list("indexers", &self.indexers, PropertyModel::to_json)
915            .list("registered", &self.registered, RegisteredModel::to_json)
916            .list("methods", &self.methods, MemberModel::to_json)
917            .list("fields", &self.fields, MemberModel::to_json)
918            .list("events", &self.events, MemberModel::to_json)
919            .list("enum_members", &self.enum_members, EnumMemberModel::to_json)
920            .flag("is_flags", self.is_flags);
921        Json::object(
922            attributes_to_json(members, &self.attributes)
923                .list("property_attributes", &self.property_attributes, |(name, attributes)| {
924                    Json::Array(vec![Json::string(name), Json::Array(attributes.iter().map(AttributeModel::to_json).collect())])
925                })
926                .optional("notify_property_changed", optional_type(&self.notify_property_changed)),
927        )
928    }
929
930    fn from_json(value: &Json) -> Result<Self, String> {
931        let fields = Fields::of(value, "a type")?;
932        Ok(Self {
933            namespace: fields.text_or_empty("namespace")?,
934            explicit_namespace: fields.optional_text("explicit_namespace")?,
935            name: fields.text("name")?,
936            kind: TypeKind::of(&fields.text("kind")?)?,
937            object_model: fields.flag("object_model")?,
938            class_markup: fields.flag("class_markup")?,
939            unregistered: fields.flag("unregistered")?,
940            rust_path: fields.optional("rust_path", RustType::from_json)?.ok_or_else(|| "\"rust_path\" is missing in a type".to_string())?,
941            public_path: fields.optional_text("public_path")?,
942            stated_path: fields.optional_text("stated_path")?,
943            module: fields.text_or_empty("module")?,
944            cfg: fields.list("cfg", |condition| text_of(condition, "a cfg condition"))?,
945            handles: fields.list("handles", RustType::from_json)?,
946            this: fields.optional("this", RustType::from_json)?,
947            base: fields.optional("base", RustType::from_json)?,
948            interfaces: fields.list("interfaces", RustType::from_json)?,
949            generic: fields.optional("generic", |generic| {
950                let generic = Fields::of(generic, "a generic definition")?;
951                Ok(GenericModel { definition: generic.text("definition")?, arguments: generic.list("arguments", RustType::from_json)? })
952            })?,
953            type_info: fields.optional("type_info", RustType::from_json)?,
954            default_constructor: fields.optional("default_constructor", CallableModel::from_json)?,
955            content_property: fields.optional_text("content_property")?,
956            parse: fields.optional("parse", CallableModel::from_json)?,
957            parse_call: fields.optional("parse_call", CallForm::from_json)?,
958            constructors: fields.list("constructors", MemberModel::from_json)?,
959            properties: fields.list("properties", PropertyModel::from_json)?,
960            static_properties: fields.list("static_properties", PropertyModel::from_json)?,
961            indexers: fields.list("indexers", PropertyModel::from_json)?,
962            registered: fields.list("registered", RegisteredModel::from_json)?,
963            methods: fields.list("methods", MemberModel::from_json)?,
964            fields: fields.list("fields", MemberModel::from_json)?,
965            events: fields.list("events", MemberModel::from_json)?,
966            enum_members: fields.list("enum_members", EnumMemberModel::from_json)?,
967            is_flags: fields.flag("is_flags")?,
968            attributes: fields.list("attributes", AttributeModel::from_json)?,
969            property_attributes: fields.list("property_attributes", |pair| match pair {
970                Json::Array(pair) if pair.len() == 2 => {
971                    let attributes = match &pair[1] {
972                        Json::Array(attributes) => attributes.iter().map(AttributeModel::from_json).collect::<Result<Vec<_>, _>>()?,
973                        _ => return Err("the attributes of a registered property are not an array".to_string()),
974                    };
975                    Ok((text_of(&pair[0], "the name of a registered property")?, attributes))
976                }
977                _ => Err("the attributes of a registered property are not a pair".to_string()),
978            })?,
979            notify_property_changed: fields.optional("notify_property_changed", RustType::from_json)?,
980        })
981    }
982}
983
984/// `XmlnsDefinition` of the assembly: an XML namespace and a namespace it maps to.
985#[derive(Clone, Debug, PartialEq, Eq)]
986pub struct XmlnsDefinitionModel {
987    pub xml_namespace: String,
988    pub namespace: String,
989}
990
991/// `XmlnsPrefix` of the assembly: the prefix recommended for an XML namespace.
992#[derive(Clone, Debug, PartialEq, Eq)]
993pub struct XmlnsPrefixModel {
994    pub xml_namespace: String,
995    pub prefix: String,
996}
997
998/// A path another crate can name a type of the crate by.
999#[derive(Clone, Debug, PartialEq, Eq)]
1000pub struct ExportModel {
1001    /// The public path (`::ferroui_controls::Border`).
1002    pub path: String,
1003    /// The path of the declaring module and the name (`::ferroui_controls::border::Border`);
1004    /// a path into another crate for a type the crate exports again.
1005    pub declared: String,
1006}
1007
1008/// A type alias without parameters of a crate (`pub type PageList = FerroList<Ref<Page>>;`):
1009/// the two texts are one Rust type.
1010#[derive(Clone, Debug, PartialEq, Eq)]
1011pub struct AliasModel {
1012    /// The path of the declaring module and the name (`::ferroui_controls::page::multi_page::PageList`).
1013    pub path: String,
1014    /// The type the alias stands for.
1015    pub target: RustType,
1016}
1017
1018/// One more Rust type that holds a value of a type with markup metadata, registered by a
1019/// crate next to the `handles:` of the declaration
1020/// (`MarkupType::register_handle::<Handle>(<Type as MarkupTyped>::MARKUP)`): the wrapper a
1021/// property of another crate holds the value in.
1022#[derive(Clone, Debug, PartialEq, Eq)]
1023pub struct HandleModel {
1024    pub handle: RustType,
1025    /// The Rust type of the type with the metadata, as its declaration has it
1026    /// ([`TypeModel::rust_path`]).
1027    pub type_: RustType,
1028}
1029
1030/// A cast a crate registers between two Rust types (`ValueTypes::register_cast::<From, To>(..)`):
1031/// a value of the one is a value of the other. A collection that declares an
1032/// instantiation of the notifying list as its `base:` is that list only when the cast from
1033/// the collection to the list is registered.
1034#[derive(Clone, Debug, PartialEq, Eq)]
1035pub struct CastModel {
1036    pub from: RustType,
1037    pub to: RustType,
1038}
1039
1040/// A macro the crate exports (`#[macro_export]`) that declares through a declaration
1041/// macro: what a crate built on this one declares by invoking it
1042/// (`ferroui_controls::ferro_markup_list!(pub Names: String);`). The scan of that crate
1043/// expands the invocation with the rules, as it expands the macros of its own crate.
1044#[derive(Clone, Debug, PartialEq, Eq)]
1045pub struct MacroModel {
1046    pub name: String,
1047    /// The rules, each the text of the tokens of its matcher and of its transcriber, with
1048    /// `$crate` written as it is.
1049    pub rules: Vec<(String, String)>,
1050}
1051
1052/// A registration of Rust types with the untyped value conversions that a function of a
1053/// crate makes with its types stated (`ValueTypes::register_nullable::<T>()`): what
1054/// decides, next to the casts and the declarations, whether a value of one Rust type is a
1055/// value of another, and which types are nullable forms.
1056#[derive(Clone, Debug, PartialEq, Eq)]
1057pub struct ValueTypeModel {
1058    /// The registration function, without `register_`: `nullable` (`Option<T>` is the
1059    /// nullable form of `T`), `reference` (`T` is a shared object held as `Rc<T>` and
1060    /// `Option<Rc<T>>`), `object` (`Ref<T>` is the handle of the class `T`), `element_ref`
1061    /// (`ElementRef<T>` of the class `T`), `interface` (the handles of the class `T`
1062    /// convert to the contract handle `I`), `upcast` (the handles of the class `T` to the
1063    /// ones of its base), `deref` (a reference to the first type coerces to a reference
1064    /// to the second: no conversion, the fact generated code relies on when it passes a
1065    /// named collection where a member of the list it derives from takes the list).
1066    pub registration: String,
1067    /// The type arguments of the call, in order.
1068    pub types: Vec<RustType>,
1069}
1070
1071/// The registration functions of the untyped value conversions the scanner reads
1072/// ([`ValueTypeModel`], and `cast` for [`CastModel`]), with the number of type arguments.
1073pub const VALUE_REGISTRATIONS: &[(&str, usize)] =
1074    &[("nullable", 1), ("reference", 1), ("object", 1), ("element_ref", 1), ("interface", 2), ("upcast", 2), ("cast", 2), ("deref", 2)];
1075
1076/// A public function of an inherent `impl` block of a type of a crate, as far as the choice
1077/// of a call form reads it (9.5.3, form B).
1078#[derive(Clone, Debug, Default, PartialEq, Eq)]
1079pub struct FunctionModel {
1080    pub name: String,
1081    /// Whether the function takes `self`.
1082    pub receiver: bool,
1083    /// The number of parameters after `self`.
1084    pub parameters: usize,
1085    /// The declaration macro that writes the function (`ferro_routed_event`), when one does.
1086    pub declared_by: Option<String>,
1087}
1088
1089/// The public functions of the inherent `impl` blocks of one type of a crate
1090/// (`FunctionsModel`): what a callable of a crate built on this one is looked up in, so
1091/// that a member declared there with a function of this crate is called by its path.
1092#[derive(Clone, Debug, Default, PartialEq, Eq)]
1093pub struct FunctionsModel {
1094    /// The Rust path of the type, by its declaring module.
1095    pub owner: String,
1096    /// The shortest path another crate names the type by.
1097    pub public_path: String,
1098    /// The functions, in the order of the scan.
1099    pub functions: Vec<FunctionModel>,
1100}
1101
1102impl FunctionsModel {
1103    fn to_json(&self) -> Json {
1104        let function = |function: &FunctionModel| {
1105            let mut parts = vec![Json::string(&function.name), Json::Integer(function.parameters as i64)];
1106            if function.receiver || function.declared_by.is_some() {
1107                parts.push(Json::Bool(function.receiver));
1108            }
1109            if let Some(declared_by) = &function.declared_by {
1110                parts.push(Json::string(declared_by));
1111            }
1112            Json::Array(parts)
1113        };
1114        Json::Array(vec![Json::string(&self.owner), Json::string(&self.public_path), Json::Array(self.functions.iter().map(function).collect())])
1115    }
1116
1117    fn from_json(value: &Json) -> Result<Self, String> {
1118        let function = |value: &Json| match value {
1119            Json::Array(parts) if (2..=4).contains(&parts.len()) => Ok(FunctionModel {
1120                name: text_of(&parts[0], "the name of a function")?,
1121                parameters: match &parts[1] {
1122                    Json::Integer(count) if *count >= 0 => *count as usize,
1123                    _ => return Err("the number of parameters of a function is not a number".to_string()),
1124                },
1125                receiver: match parts.get(2) {
1126                    None => false,
1127                    Some(Json::Bool(receiver)) => *receiver,
1128                    Some(_) => return Err("the receiver of a function is not a flag".to_string()),
1129                },
1130                declared_by: parts.get(3).map(|declared_by| text_of(declared_by, "the macro of a function")).transpose()?,
1131            }),
1132            _ => Err("a function is not a list of two to four parts".to_string()),
1133        };
1134        match value {
1135            Json::Array(parts) if parts.len() == 3 => Ok(Self {
1136                owner: text_of(&parts[0], "the owner of functions")?,
1137                public_path: text_of(&parts[1], "the public path of the owner of functions")?,
1138                functions: match &parts[2] {
1139                    Json::Array(functions) => functions.iter().map(function).collect::<Result<_, _>>()?,
1140                    _ => return Err("the functions of a type are not a list".to_string()),
1141                },
1142            }),
1143            _ => Err("the functions of a type are not a list of three parts".to_string()),
1144        }
1145    }
1146}
1147
1148fn pair_to_json(first: &str, second: &str) -> Json {
1149    Json::Array(vec![Json::string(first), Json::string(second)])
1150}
1151
1152fn pair_from_json(value: &Json, what: &str) -> Result<(String, String), String> {
1153    match value {
1154        Json::Array(pair) if pair.len() == 2 => Ok((text_of(&pair[0], what)?, text_of(&pair[1], what)?)),
1155        _ => Err(format!("{what} is not a pair")),
1156    }
1157}
1158
1159/// What a crate states about itself for markup (`AssemblyModel`).
1160#[derive(Clone, Debug, PartialEq, Eq)]
1161pub struct AssemblyModel {
1162    /// The assembly name.
1163    pub name: String,
1164    /// The name of the crate, as Rust paths spell it.
1165    pub crate_name: String,
1166    pub xmlns_definitions: Vec<XmlnsDefinitionModel>,
1167    pub xmlns_prefixes: Vec<XmlnsPrefixModel>,
1168    /// The metadata of the assembly (`FerroXamlCreateSourceInfo`), name and value.
1169    pub metadata: Vec<(String, String)>,
1170    /// The namespace table: a module path and the dotted namespace of the types declared
1171    /// below it. A type belongs to the namespace of the longest module path that is a
1172    /// prefix of the path of its module.
1173    pub namespaces: Vec<(String, String)>,
1174    pub types: Vec<TypeModel>,
1175    /// The export table of the crate: every path another crate can name a type of the
1176    /// crate by, with the path of the module that declares it (9.5.2, step 1). What a glob
1177    /// import of a module of the crate brings into another crate, and what makes a path
1178    /// into the crate canonical, is read from it.
1179    pub exports: Vec<ExportModel>,
1180    /// The type aliases of the crate whose type the scanner resolved.
1181    pub aliases: Vec<AliasModel>,
1182    /// The handles the crate registers for types with markup metadata, of this crate or
1183    /// of the crates it is built on.
1184    pub handles: Vec<HandleModel>,
1185    /// The casts the crate registers between Rust types, in the order of the scan.
1186    pub casts: Vec<CastModel>,
1187    /// The other registrations with the untyped value conversions the functions of the
1188    /// crate make with their types stated.
1189    pub value_types: Vec<ValueTypeModel>,
1190    /// The registrations the text of the crate has and the model does not: for each
1191    /// registration function ([`VALUE_REGISTRATIONS`]) with such calls, their number (a call
1192    /// that leaves a type to inference, a call in the body of a macro, in a macro
1193    /// invocation or in test code). A crate with such calls registers more than its model
1194    /// states, so nothing follows from a registration that is not in the model.
1195    pub unread_value_types: Vec<(String, i64)>,
1196    /// The macros the crate exports that declare through a declaration macro.
1197    pub macros: Vec<MacroModel>,
1198    /// The public functions of the inherent `impl` blocks of the types of the crate that
1199    /// another crate can name, by type, in the order of the scan.
1200    pub functions: Vec<FunctionsModel>,
1201    /// The compiled documents, in the order of the compilation.
1202    pub documents: Vec<DocumentModel>,
1203    /// The `.xamlmeta` files of the crates this crate is built on, relative to the
1204    /// directory of this file or absolute.
1205    pub dependencies: Vec<String>,
1206}
1207
1208impl AssemblyModel {
1209    /// A model without types and documents.
1210    pub fn new(name: &str, crate_name: &str) -> Self {
1211        Self {
1212            name: name.to_string(),
1213            crate_name: crate_name.to_string(),
1214            xmlns_definitions: Vec::new(),
1215            xmlns_prefixes: Vec::new(),
1216            metadata: Vec::new(),
1217            namespaces: Vec::new(),
1218            types: Vec::new(),
1219            exports: Vec::new(),
1220            aliases: Vec::new(),
1221            handles: Vec::new(),
1222            casts: Vec::new(),
1223            value_types: Vec::new(),
1224            unread_value_types: Vec::new(),
1225            macros: Vec::new(),
1226            functions: Vec::new(),
1227            documents: Vec::new(),
1228            dependencies: Vec::new(),
1229        }
1230    }
1231
1232    /// The model of a file of format 1: the documents of a crate, no types.
1233    pub fn from_metadata(metadata: XamlMetadata) -> Self {
1234        let mut model = Self::new(&metadata.name, &metadata.crate_name);
1235        model.documents = metadata.documents;
1236        model.dependencies = metadata.dependencies;
1237        model
1238    }
1239
1240    /// The part of the model a file of format 1 holds: what the compiler reads today.
1241    pub fn metadata(&self) -> XamlMetadata {
1242        XamlMetadata {
1243            name: self.name.clone(),
1244            crate_name: self.crate_name.clone(),
1245            documents: self.documents.clone(),
1246            dependencies: self.dependencies.clone(),
1247        }
1248    }
1249
1250    /// The namespace the table gives the module `module`; empty when it has none.
1251    pub fn namespace_of_module(&self, module: &str) -> &str {
1252        let mut best: Option<(&str, &str)> = None;
1253        for (prefix, namespace) in &self.namespaces {
1254            let matches = match module.strip_prefix(prefix.as_str()) {
1255                Some(rest) => rest.is_empty() || rest.starts_with("::"),
1256                None => false,
1257            };
1258            if matches && best.is_none_or(|(known, _)| prefix.len() > known.len()) {
1259                best = Some((prefix.as_str(), namespace.as_str()));
1260            }
1261        }
1262        best.map_or("", |(_, namespace)| namespace)
1263    }
1264
1265    /// The type with the namespace-qualified name `full_name`.
1266    pub fn find_type(&self, full_name: &str) -> Option<&TypeModel> {
1267        self.types.iter().find(|type_| type_.full_name() == full_name)
1268    }
1269
1270    /// The type with the Rust type text `rust_path` (its path, for a type the crate declares).
1271    pub fn find_rust_type(&self, rust_path: &str) -> Option<&TypeModel> {
1272        self.types.iter().find(|type_| type_.rust_path.text == rust_path)
1273    }
1274
1275    /// Calls `visit` with every type text of the model that is not of one of its types: the
1276    /// types of its aliases, its registered handles and its registered casts.
1277    pub fn visit_types_mut(&mut self, visit: &mut dyn FnMut(&mut RustType)) {
1278        for alias in &mut self.aliases {
1279            visit(&mut alias.target);
1280        }
1281        for handle in &mut self.handles {
1282            visit(&mut handle.handle);
1283            visit(&mut handle.type_);
1284        }
1285        for cast in &mut self.casts {
1286            visit(&mut cast.from);
1287            visit(&mut cast.to);
1288        }
1289        for registration in &mut self.value_types {
1290            for type_ in &mut registration.types {
1291                visit(type_);
1292            }
1293        }
1294    }
1295
1296    /// The model as the text of a `.xamlmeta` file of format 2.
1297    pub fn to_json(&self) -> String {
1298        let document = |document: &DocumentModel| {
1299            let optional = |text: &Option<String>| text.as_deref().map_or(Json::Null, Json::string);
1300            Json::Object(vec![
1301                ("uri".to_string(), Json::string(&document.uri)),
1302                ("root_type".to_string(), Json::string(&document.root_type)),
1303                ("class_rust_path".to_string(), optional(&document.class_rust_path)),
1304                ("build_path".to_string(), optional(&document.build_path)),
1305                ("populate_path".to_string(), optional(&document.populate_path)),
1306                ("public".to_string(), Json::Bool(document.public)),
1307            ])
1308        };
1309        let members = Members::new()
1310            .always("format", Json::Integer(FORMAT))
1311            .text("name", &self.name)
1312            .text("crate_name", &self.crate_name)
1313            .always("documents", Json::Array(self.documents.iter().map(document).collect()))
1314            .always("dependencies", Json::Array(self.dependencies.iter().map(|path| Json::string(path)).collect()))
1315            .list("xmlns_definitions", &self.xmlns_definitions, |definition| pair_to_json(&definition.xml_namespace, &definition.namespace))
1316            .list("xmlns_prefixes", &self.xmlns_prefixes, |prefix| pair_to_json(&prefix.xml_namespace, &prefix.prefix))
1317            .list("metadata", &self.metadata, |(name, value)| pair_to_json(name, value))
1318            .list("namespaces", &self.namespaces, |(module, namespace)| pair_to_json(module, namespace))
1319            .list("types", &self.types, TypeModel::to_json)
1320            .list("exports", &self.exports, |export| pair_to_json(&export.path, &export.declared))
1321            .list("aliases", &self.aliases, |alias| Json::Array(vec![Json::string(&alias.path), alias.target.to_json()]))
1322            .list("handles", &self.handles, |handle| Json::Array(vec![handle.handle.to_json(), handle.type_.to_json()]))
1323            .list("casts", &self.casts, |cast| Json::Array(vec![cast.from.to_json(), cast.to.to_json()]))
1324            .list("value_types", &self.value_types, |registration| {
1325                let mut parts = vec![Json::string(&registration.registration)];
1326                parts.extend(registration.types.iter().map(RustType::to_json));
1327                Json::Array(parts)
1328            })
1329            .list("unread_value_types", &self.unread_value_types, |(registration, count)| Json::Array(vec![Json::string(registration), Json::Integer(*count)]))
1330            .list("macros", &self.macros, |exported| {
1331                let rules = exported.rules.iter().map(|(matcher, transcriber)| pair_to_json(matcher, transcriber)).collect();
1332                Json::Array(vec![Json::string(&exported.name), Json::Array(rules)])
1333            })
1334            .list("functions", &self.functions, FunctionsModel::to_json);
1335        Json::object(members).to_text()
1336    }
1337
1338    /// Reads the model from the text of a `.xamlmeta` file of format 1 or 2.
1339    pub fn parse(text: &str) -> Result<Self, String> {
1340        let value = Json::parse(text)?;
1341        let fields = Fields::of(&value, "the file")?;
1342        let format = fields.optional_integer("format")?.unwrap_or(1);
1343        if !(1..=FORMAT).contains(&format) {
1344            return Err(format!("the file has the format {format}; this reader reads the formats 1 to {FORMAT}"));
1345        }
1346        let documents = fields.list("documents", |document| {
1347            let document = Fields::of(document, "a document")?;
1348            Ok(DocumentModel {
1349                uri: document.text("uri")?,
1350                root_type: document.text("root_type")?,
1351                class_rust_path: document.optional_text("class_rust_path")?,
1352                build_path: document.optional_text("build_path")?,
1353                populate_path: document.optional_text("populate_path")?,
1354                public: document.flag("public")?,
1355            })
1356        })?;
1357        Ok(Self {
1358            name: fields.text("name")?,
1359            crate_name: fields.text("crate_name")?,
1360            xmlns_definitions: fields.list("xmlns_definitions", |pair| {
1361                pair_from_json(pair, "an xmlns definition").map(|(xml_namespace, namespace)| XmlnsDefinitionModel { xml_namespace, namespace })
1362            })?,
1363            xmlns_prefixes: fields.list("xmlns_prefixes", |pair| {
1364                pair_from_json(pair, "an xmlns prefix").map(|(xml_namespace, prefix)| XmlnsPrefixModel { xml_namespace, prefix })
1365            })?,
1366            metadata: fields.list("metadata", |pair| pair_from_json(pair, "an entry of the metadata"))?,
1367            namespaces: fields.list("namespaces", |pair| pair_from_json(pair, "an entry of the namespace table"))?,
1368            types: fields.list("types", TypeModel::from_json)?,
1369            exports: fields.list("exports", |pair| pair_from_json(pair, "an entry of the export table").map(|(path, declared)| ExportModel { path, declared }))?,
1370            aliases: fields.list("aliases", |pair| match pair {
1371                Json::Array(pair) if pair.len() == 2 => Ok(AliasModel { path: text_of(&pair[0], "the path of an alias")?, target: RustType::from_json(&pair[1])? }),
1372                _ => Err("a type alias is not a pair".to_string()),
1373            })?,
1374            handles: fields.list("handles", |pair| match pair {
1375                Json::Array(pair) if pair.len() == 2 => Ok(HandleModel { handle: RustType::from_json(&pair[0])?, type_: RustType::from_json(&pair[1])? }),
1376                _ => Err("a registered handle is not a pair".to_string()),
1377            })?,
1378            casts: fields.list("casts", |pair| match pair {
1379                Json::Array(pair) if pair.len() == 2 => Ok(CastModel { from: RustType::from_json(&pair[0])?, to: RustType::from_json(&pair[1])? }),
1380                _ => Err("a registered cast is not a pair".to_string()),
1381            })?,
1382            value_types: fields.list("value_types", |parts| match parts {
1383                Json::Array(parts) if !parts.is_empty() => Ok(ValueTypeModel {
1384                    registration: text_of(&parts[0], "the name of a registration")?,
1385                    types: parts[1..].iter().map(RustType::from_json).collect::<Result<_, _>>()?,
1386                }),
1387                _ => Err("a registration of value types is not a list".to_string()),
1388            })?,
1389            unread_value_types: fields.list("unread_value_types", |pair| match pair {
1390                Json::Array(pair) if pair.len() == 2 => match &pair[1] {
1391                    Json::Integer(count) => Ok((text_of(&pair[0], "the name of a registration")?, *count)),
1392                    _ => Err("the number of registrations that are not read is not an integer".to_string()),
1393                },
1394                _ => Err("an entry of the registrations that are not read is not a pair".to_string()),
1395            })?,
1396            macros: fields.list("macros", |exported| match exported {
1397                Json::Array(parts) if parts.len() == 2 => match &parts[1] {
1398                    Json::Array(rules) => Ok(MacroModel {
1399                        name: text_of(&parts[0], "the name of a macro")?,
1400                        rules: rules.iter().map(|rule| pair_from_json(rule, "a rule of a macro")).collect::<Result<_, _>>()?,
1401                    }),
1402                    _ => Err("the rules of a macro are not a list".to_string()),
1403                },
1404                _ => Err("an exported macro is not a pair".to_string()),
1405            })?,
1406            functions: fields.list("functions", FunctionsModel::from_json)?,
1407            documents,
1408            dependencies: fields.list("dependencies", |path| text_of(path, "a dependency"))?,
1409        })
1410    }
1411}
1412
1413#[cfg(test)]
1414mod tests {
1415    use super::*;
1416    use std::path::Path;
1417
1418    fn callable(path: &str, resolved: Option<&str>) -> CallableModel {
1419        CallableModel { path: Some(path.to_string()), resolved: resolved.map(str::to_string), dereferenced: None }
1420    }
1421
1422    /// A model with every member of every part set to something that is not its default.
1423    fn full_model() -> AssemblyModel {
1424        let attribute = AttributeModel {
1425            name: "TemplatePart".to_string(),
1426            arguments: vec![
1427                AttributeValueModel::Str("PART_\"Bar\"".to_string()),
1428                AttributeValueModel::Type(RustType::resolved("::ferroui_base::Ref<::fixture::Border>")),
1429                AttributeValueModel::Array(vec![AttributeValueModel::Int(-1), AttributeValueModel::Null, AttributeValueModel::Float("1.5".to_string())]),
1430            ],
1431            properties: vec![("IsRequired".to_string(), AttributeValueModel::Bool(true))],
1432        };
1433        let unresolved = RustType { text: "Option<Mystery>".to_string(), unresolved: vec!["Mystery".to_string()] };
1434        let mut type_ = TypeModel::new("Panel", TypeKind::Class, RustType::resolved("::fixture::panel::Panel"), "fixture::panel");
1435        type_.namespace = "Fixture.Controls".to_string();
1436        type_.explicit_namespace = Some("Fixture.Controls".to_string());
1437        type_.object_model = true;
1438        type_.unregistered = true;
1439        type_.public_path = Some("::fixture::Panel".to_string());
1440        type_.cfg = vec!["feature = \"panels\"".to_string()];
1441        type_.handles = vec![RustType::resolved("::fixture::panel::Panel"), unresolved.clone()];
1442        type_.this = Some(RustType::resolved("::std::rc::Rc<::fixture::panel::Panel>"));
1443        type_.base = Some(RustType::resolved("::fixture::control::Control"));
1444        type_.interfaces = vec![RustType::resolved("::std::rc::Rc<dyn ::fixture::IPanel>")];
1445        type_.generic = Some(GenericModel { definition: "FerroList`1".to_string(), arguments: vec![RustType::resolved("f64")] });
1446        type_.type_info = Some(RustType::resolved("::fixture::panel::Panel"));
1447        type_.default_constructor = Some(callable("Panel::new", Some("::fixture::panel::Panel::new")));
1448        type_.content_property = Some("Children".to_string());
1449        type_.parse = Some(CallableModel::default());
1450        type_.parse_call = Some(CallForm::CratePath("::fixture::Panel::parse".to_string()));
1451        type_.constructors = vec![MemberModel {
1452            parameters: vec![
1453                ParameterModel { name: Some("name".to_string()), type_: RustType::resolved("String"), attributes: vec![attribute.clone()] },
1454                ParameterModel { name: None, type_: unresolved.clone(), attributes: Vec::new() },
1455            ],
1456            is_static: true,
1457            fallible: true,
1458            callable: callable("Panel::with_name", None),
1459            typed_function: Some("__markup_new_0".to_string()),
1460            call: Some(CallForm::Path("::fixture::Panel::with_name".to_string())),
1461            ..MemberModel::default()
1462        }];
1463        let accessor = AccessorModel {
1464            fallible: true,
1465            callable: CallableModel::default(),
1466            typed_function: Some("__markup_get_Children".to_string()),
1467            call: Some(CallForm::Invoker),
1468        };
1469        type_.properties = vec![PropertyModel {
1470            name: "Children".to_string(),
1471            parameters: Vec::new(),
1472            value_type: RustType::resolved("::fixture::Controls"),
1473            getter: Some(accessor.clone()),
1474            setter: None,
1475            attributes: vec![attribute.clone()],
1476        }];
1477        type_.static_properties = vec![PropertyModel {
1478            name: "Default".to_string(),
1479            parameters: Vec::new(),
1480            value_type: RustType::resolved("i32"),
1481            getter: None,
1482            setter: Some(AccessorModel { call: Some(CallForm::Structural), ..AccessorModel::default() }),
1483            attributes: Vec::new(),
1484        }];
1485        type_.indexers = vec![PropertyModel {
1486            name: String::new(),
1487            parameters: vec![ParameterModel { name: None, type_: RustType::resolved("i32"), attributes: Vec::new() }],
1488            value_type: RustType::resolved("String"),
1489            getter: Some(accessor),
1490            setter: None,
1491            attributes: Vec::new(),
1492        }];
1493        type_.registered = vec![
1494            RegisteredModel {
1495                name: Some("Row".to_string()),
1496                kind: RegisteredKind::Attached,
1497                value_type: RustType::resolved("i32"),
1498                owner: Some(RustType::resolved("::fixture::panel::Panel")),
1499                host: Some(RustType::resolved("::fixture::control::Control")),
1500                accessor: "row_property".to_string(),
1501                function_of: Some("::fixture::grid::Grid".to_string()),
1502                visibility: "pub".to_string(),
1503                registration: RegistrationModel::Declared,
1504                source: None,
1505                assign_binding: true,
1506                inherits: true,
1507                read_only: false,
1508                added_owners: vec!["::fixture::grid::Grid".to_string()],
1509            },
1510            RegisteredModel {
1511                name: None,
1512                kind: RegisteredKind::Direct,
1513                value_type: unresolved.clone(),
1514                owner: None,
1515                host: None,
1516                accessor: "text_property".to_string(),
1517                function_of: None,
1518                visibility: String::new(),
1519                registration: RegistrationModel::AddedOwner,
1520                source: Some(callable("TextBlock::text_property", None)),
1521                assign_binding: false,
1522                inherits: false,
1523                read_only: true,
1524                added_owners: Vec::new(),
1525            },
1526        ];
1527        type_.methods = vec![MemberModel {
1528            name: "Add".to_string(),
1529            parameters: vec![ParameterModel { name: None, type_: RustType::resolved("::fixture::Control"), attributes: Vec::new() }],
1530            return_type: Some(RustType::resolved("bool")),
1531            attributes: vec![attribute.clone()],
1532            callable: CallableModel::default(),
1533            typed_function: Some("__markup_Add_0".to_string()),
1534            ..MemberModel::default()
1535        }];
1536        type_.fields = vec![MemberModel {
1537            name: "ClickEvent".to_string(),
1538            return_type: Some(unresolved.clone()),
1539            is_static: true,
1540            callable: CallableModel { path: None, resolved: None, dereferenced: Some("::fixture::panel::Panel::click_event".to_string()) },
1541            ..MemberModel::default()
1542        }];
1543        type_.events = vec![MemberModel { name: "Closed".to_string(), fallible: true, ..MemberModel::default() }];
1544        type_.enum_members = vec![
1545            EnumMemberModel { name: "Self".to_string(), rust_variant: Some("Self_".to_string()), rust_value: None, value: Some(2) },
1546            EnumMemberModel { name: "Direct".to_string(), rust_variant: None, rust_value: Some("Routing::DIRECT".to_string()), value: None },
1547        ];
1548        type_.is_flags = true;
1549        type_.attributes = vec![attribute.clone()];
1550        type_.property_attributes = vec![("Row".to_string(), vec![attribute])];
1551        type_.notify_property_changed = Some(unresolved);
1552
1553        let mut model = AssemblyModel::new("\u{30a2}\u{30bb}\u{30f3}\u{30d6}\u{30ea}", "fixture");
1554        model.xmlns_definitions = vec![XmlnsDefinitionModel { xml_namespace: "https://example.org/fixture".to_string(), namespace: "Fixture".to_string() }];
1555        model.xmlns_prefixes = vec![XmlnsPrefixModel { xml_namespace: "https://example.org/fixture".to_string(), prefix: "f".to_string() }];
1556        model.metadata = vec![("FerroXamlCreateSourceInfo".to_string(), "true".to_string())];
1557        model.namespaces = vec![("fixture".to_string(), "Fixture".to_string()), ("fixture::panel".to_string(), "Fixture.Controls".to_string())];
1558        model.types = vec![type_, TypeModel::new("Dock", TypeKind::Enum, RustType::resolved("::fixture::Dock"), "fixture")];
1559        model.exports = vec![ExportModel { path: "::fixture::Panel".to_string(), declared: "::fixture::panel::Panel".to_string() }];
1560        model.aliases = vec![AliasModel { path: "::fixture::panel::Panels".to_string(), target: RustType::resolved("Vec<::fixture::panel::Panel>") }];
1561        model.handles = vec![HandleModel {
1562            handle: RustType { text: "Option<Wrapper>".to_string(), unresolved: vec!["Wrapper".to_string()] },
1563            type_: RustType::resolved("dyn ::fixture::IPanel"),
1564        }];
1565        model.casts = vec![CastModel { from: RustType::resolved("::fixture::panel::Panels"), to: RustType::resolved("Vec<::fixture::panel::Panel>") }];
1566        model.functions = vec![FunctionsModel {
1567            owner: "::fixture::panel::Panel".to_string(),
1568            public_path: "::fixture::Panel".to_string(),
1569            functions: vec![
1570                FunctionModel { name: "new".to_string(), ..FunctionModel::default() },
1571                FunctionModel { name: "add".to_string(), receiver: true, parameters: 1, declared_by: None },
1572                FunctionModel { name: "click_event".to_string(), receiver: false, parameters: 0, declared_by: Some("ferro_routed_event".to_string()) },
1573            ],
1574        }];
1575        model.documents = vec![DocumentModel {
1576            uri: "ferres://Fixture/Main.xaml".to_string(),
1577            root_type: "Fixture.Controls.Panel".to_string(),
1578            class_rust_path: Some("::fixture::Main".to_string()),
1579            build_path: None,
1580            populate_path: Some("::fixture::compiled_xaml::populate".to_string()),
1581            public: true,
1582        }];
1583        model.dependencies = vec!["../Other/compiled_xaml.xamlmeta".to_string()];
1584        model
1585    }
1586
1587    /// Not from upstream: the model is read back as it was written, whatever is set in it,
1588    /// and the text is stable.
1589    #[test]
1590    fn model_round_trips_through_its_text() {
1591        let model = full_model();
1592        let text = model.to_json();
1593        assert_eq!(AssemblyModel::parse(&text), Ok(model), "{text}");
1594        assert_eq!(AssemblyModel::parse(&text).map(|read| read.to_json()), Ok(text.clone()));
1595        let empty = AssemblyModel::new("A", "a");
1596        assert_eq!(AssemblyModel::parse(&empty.to_json()), Ok(empty.clone()));
1597        assert_eq!(empty.to_json(), "{\n  \"format\": 2,\n  \"name\": \"A\",\n  \"crate_name\": \"a\",\n  \"documents\": [],\n  \"dependencies\": []\n}\n");
1598    }
1599
1600    /// Not from upstream: the reader of format 1 (the compiler's) reads a file of format 2
1601    /// as the documents of the crate, and a file of format 1 is a model without types.
1602    #[test]
1603    fn both_formats_are_read_by_both_readers() {
1604        let model = full_model();
1605        assert_eq!(XamlMetadata::parse(&model.to_json()), Ok(model.metadata()));
1606        let first = model.metadata().to_json();
1607        assert!(!first.contains("\"format\""), "{first}");
1608        let read = AssemblyModel::parse(&first).expect("a file of format 1");
1609        assert_eq!(read, AssemblyModel::from_metadata(model.metadata()));
1610        assert!(read.types.is_empty() && read.namespaces.is_empty());
1611        assert_eq!(read.documents, model.documents);
1612        let newer = model.to_json().replacen("\"format\": 2", "\"format\": 3", 1);
1613        assert_eq!(AssemblyModel::parse(&newer), Err("the file has the format 3; this reader reads the formats 1 to 2".to_string()));
1614    }
1615
1616    /// Not from upstream: the checked-in `.xamlmeta` of the themes (format 1, written by the
1617    /// emitter) is read, and written back in format 2 it gives the compiler the same
1618    /// documents.
1619    #[test]
1620    fn checked_in_metadata_of_the_themes_is_read() {
1621        let source = Path::new(env!("CARGO_MANIFEST_DIR")).parent().expect("the directory of the crates");
1622        for (directory, name, crate_name, class) in [
1623            ("FerroUI.Themes.Simple", "FerroUI.Themes.Simple", "ferroui_themes_simple", "::ferroui_themes_simple::SimpleTheme"),
1624            ("FerroUI.Themes.Fluent", "FerroUI.Themes.Fluent", "ferroui_themes_fluent", "::ferroui_themes_fluent::FluentTheme"),
1625        ] {
1626            let path = source.join(directory).join("compiled_xaml.xamlmeta");
1627            let text = std::fs::read_to_string(&path).unwrap_or_else(|error| panic!("{}: {error}", path.display()));
1628            let model = AssemblyModel::parse(&text).unwrap_or_else(|error| panic!("{}: {error}", path.display()));
1629            let metadata = XamlMetadata::parse(&text).unwrap_or_else(|error| panic!("{}: {error}", path.display()));
1630            assert_eq!(model.name, name, "{}", path.display());
1631            assert_eq!(model.crate_name, crate_name, "{}", path.display());
1632            assert!(model.types.is_empty(), "{}: a file of format 1 has no types", path.display());
1633            assert!(!model.documents.is_empty(), "{}: no documents", path.display());
1634            assert_eq!(model.metadata(), metadata, "{}: the two readers disagree", path.display());
1635            assert!(
1636                model.documents.iter().any(|document| document.class_rust_path.as_deref() == Some(class)),
1637                "{}: no document of the class {class}: {:?}",
1638                path.display(),
1639                model.documents
1640            );
1641            assert_eq!(XamlMetadata::parse(&model.to_json()), Ok(metadata), "{}", path.display());
1642        }
1643    }
1644
1645    /// Not from upstream: the namespace of a module is the one of the longest prefix of its
1646    /// path, as `TypeInfo::namespace` finds it.
1647    #[test]
1648    fn namespace_is_the_one_of_the_longest_module_prefix() {
1649        let mut model = AssemblyModel::new("A", "a");
1650        model.namespaces = vec![
1651            ("a".to_string(), "A".to_string()),
1652            ("a::media".to_string(), "A.Media".to_string()),
1653            ("a::media::imaging".to_string(), "A.Media.Imaging".to_string()),
1654        ];
1655        assert_eq!(model.namespace_of_module("a"), "A");
1656        assert_eq!(model.namespace_of_module("a::border"), "A");
1657        assert_eq!(model.namespace_of_module("a::media::brush"), "A.Media");
1658        assert_eq!(model.namespace_of_module("a::media::imaging"), "A.Media.Imaging");
1659        assert_eq!(model.namespace_of_module("a::mediator"), "A");
1660        assert_eq!(model.namespace_of_module("b::media"), "");
1661    }
1662}