1use crate::json::{text_of, Fields, Json, Members};
31pub use crate::xaml_metadata::{DocumentModel, XamlMetadata};
32
33pub const FORMAT: i64 = 2;
35
36#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
43pub struct RustType {
44 pub text: String,
46 pub unresolved: Vec<String>,
49}
50
51impl RustType {
52 pub fn resolved(text: &str) -> Self {
54 Self { text: text.to_string(), unresolved: Vec::new() }
55 }
56
57 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub enum TypeKind {
84 Class,
86 Struct,
87 Enum,
88 Interface,
89 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
119pub struct CallableModel {
120 pub path: Option<String>,
122 pub resolved: Option<String>,
124 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#[derive(Clone, Debug, PartialEq, Eq)]
146pub enum CallForm {
147 Structural,
153 Path(String),
157 CratePath(String),
161 Invoker,
163}
164
165impl CallForm {
166 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#[derive(Clone, Debug, PartialEq, Eq)]
199pub enum AttributeValueModel {
200 Null,
201 Bool(bool),
202 Int(i64),
204 Float(String),
206 Str(String),
207 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
249pub struct AttributeModel {
250 pub name: String,
252 pub arguments: Vec<AttributeValueModel>,
254 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
287pub struct ParameterModel {
288 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#[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 pub fallible: bool,
332 pub attributes: Vec<AttributeModel>,
333 pub callable: CallableModel,
335 pub typed_function: Option<String>,
339 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
377pub struct AccessorModel {
378 pub fallible: bool,
380 pub callable: CallableModel,
381 pub typed_function: Option<String>,
383 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
412pub struct PropertyModel {
413 pub name: String,
414 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#[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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
475pub enum RegistrationModel {
476 Declared,
478 AddedOwner,
481 Alias,
484 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#[derive(Clone, Debug, PartialEq, Eq)]
512pub struct RegisteredModel {
513 pub name: Option<String>,
518 pub kind: RegisteredKind,
519 pub value_type: RustType,
521 pub owner: Option<RustType>,
525 pub host: Option<RustType>,
527 pub accessor: String,
531 pub function_of: Option<String>,
537 pub visibility: String,
539 pub registration: RegistrationModel,
540 pub source: Option<CallableModel>,
543 pub assign_binding: bool,
545 pub inherits: bool,
547 pub read_only: bool,
550 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
600pub struct EnumMemberModel {
601 pub name: String,
603 pub rust_variant: Option<String>,
606 pub rust_value: Option<String>,
608 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
637pub struct GenericModel {
638 pub definition: String,
639 pub arguments: Vec<RustType>,
640}
641
642#[derive(Clone, Debug, PartialEq, Eq)]
644pub struct TypeModel {
645 pub namespace: String,
648 pub explicit_namespace: Option<String>,
650 pub name: String,
652 pub kind: TypeKind,
653 pub object_model: bool,
657 pub class_markup: bool,
660 pub unregistered: bool,
666 pub rust_path: RustType,
670 pub public_path: Option<String>,
673 pub stated_path: Option<String>,
678 pub module: String,
681 pub cfg: Vec<String>,
683 pub handles: Vec<RustType>,
686 pub this: Option<RustType>,
688 pub base: Option<RustType>,
690 pub interfaces: Vec<RustType>,
692 pub generic: Option<GenericModel>,
693 pub type_info: Option<RustType>,
695 pub default_constructor: Option<CallableModel>,
697 pub content_property: Option<String>,
698 pub parse: Option<CallableModel>,
701 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 pub property_attributes: Vec<(String, Vec<AttributeModel>)>,
716 pub notify_property_changed: Option<RustType>,
718}
719
720impl TypeModel {
721 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 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 pub fn registered(&self, name: &str) -> Option<&RegisteredModel> {
773 self.registered.iter().find(|registered| registered.name.as_deref() == Some(name))
774 }
775
776 pub fn named_path(&self) -> &str {
779 self.public_path.as_deref().unwrap_or(&self.rust_path.text)
780 }
781
782 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 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#[derive(Clone, Debug, PartialEq, Eq)]
986pub struct XmlnsDefinitionModel {
987 pub xml_namespace: String,
988 pub namespace: String,
989}
990
991#[derive(Clone, Debug, PartialEq, Eq)]
993pub struct XmlnsPrefixModel {
994 pub xml_namespace: String,
995 pub prefix: String,
996}
997
998#[derive(Clone, Debug, PartialEq, Eq)]
1000pub struct ExportModel {
1001 pub path: String,
1003 pub declared: String,
1006}
1007
1008#[derive(Clone, Debug, PartialEq, Eq)]
1011pub struct AliasModel {
1012 pub path: String,
1014 pub target: RustType,
1016}
1017
1018#[derive(Clone, Debug, PartialEq, Eq)]
1023pub struct HandleModel {
1024 pub handle: RustType,
1025 pub type_: RustType,
1028}
1029
1030#[derive(Clone, Debug, PartialEq, Eq)]
1035pub struct CastModel {
1036 pub from: RustType,
1037 pub to: RustType,
1038}
1039
1040#[derive(Clone, Debug, PartialEq, Eq)]
1045pub struct MacroModel {
1046 pub name: String,
1047 pub rules: Vec<(String, String)>,
1050}
1051
1052#[derive(Clone, Debug, PartialEq, Eq)]
1057pub struct ValueTypeModel {
1058 pub registration: String,
1067 pub types: Vec<RustType>,
1069}
1070
1071pub 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
1079pub struct FunctionModel {
1080 pub name: String,
1081 pub receiver: bool,
1083 pub parameters: usize,
1085 pub declared_by: Option<String>,
1087}
1088
1089#[derive(Clone, Debug, Default, PartialEq, Eq)]
1093pub struct FunctionsModel {
1094 pub owner: String,
1096 pub public_path: String,
1098 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#[derive(Clone, Debug, PartialEq, Eq)]
1161pub struct AssemblyModel {
1162 pub name: String,
1164 pub crate_name: String,
1166 pub xmlns_definitions: Vec<XmlnsDefinitionModel>,
1167 pub xmlns_prefixes: Vec<XmlnsPrefixModel>,
1168 pub metadata: Vec<(String, String)>,
1170 pub namespaces: Vec<(String, String)>,
1174 pub types: Vec<TypeModel>,
1175 pub exports: Vec<ExportModel>,
1180 pub aliases: Vec<AliasModel>,
1182 pub handles: Vec<HandleModel>,
1185 pub casts: Vec<CastModel>,
1187 pub value_types: Vec<ValueTypeModel>,
1190 pub unread_value_types: Vec<(String, i64)>,
1196 pub macros: Vec<MacroModel>,
1198 pub functions: Vec<FunctionsModel>,
1201 pub documents: Vec<DocumentModel>,
1203 pub dependencies: Vec<String>,
1206}
1207
1208impl AssemblyModel {
1209 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 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 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 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 pub fn find_type(&self, full_name: &str) -> Option<&TypeModel> {
1267 self.types.iter().find(|type_| type_.full_name() == full_name)
1268 }
1269
1270 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 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 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(®istration.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 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 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 #[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 #[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 #[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 #[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}