Skip to main content

regxml_dict/
meta_dictionary.rs

1use std::collections::HashMap;
2
3use quick_xml::{
4    events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event},
5    Writer,
6};
7use smpte_types::Auid;
8
9use crate::{
10    definition::{
11        CharacterTypeDef, ClassDefinition, Definition, EnumerationElement, EnumerationTypeDef,
12        ExtEnumTypeDef, FixedArrayTypeDef, FloatTypeDef, IndirectTypeDef, IntegerTypeDef,
13        LensSerialFloatTypeDef, OpaqueTypeDef, PropertyDefinition, RecordMember, RecordTypeDef,
14        RenameTypeDef, SetTypeDef, StreamTypeDef, StringTypeDef, StrongReferenceTypeDef,
15        TypeDefinition, VariableArrayTypeDef, WeakReferenceTypeDef,
16    },
17    resolver::DefinitionResolver,
18    DictError,
19};
20
21const NS: &str = "http://www.smpte-ra.org/schemas/2001-1b/2013/metadict";
22
23// ── MetaDictionary ──────────────────────────────────────────────────────────
24
25/// A single RegXML metadictionary (SMPTE ST 2001-1 Annex A).
26#[derive(Debug, Default)]
27pub struct MetaDictionary {
28    pub scheme_id: String,
29    pub scheme_uri: String,
30    definitions_by_auid: HashMap<Auid, Definition>,
31    definitions_by_symbol: HashMap<String, Auid>,
32    /// class AUID → property AUIDs
33    members_of: HashMap<Auid, Vec<Auid>>,
34    /// parent class AUID → child class AUIDs
35    subclasses_of: HashMap<Auid, Vec<Auid>>,
36}
37
38impl MetaDictionary {
39    pub fn new(scheme_uri: impl Into<String>, scheme_id: impl Into<String>) -> Self {
40        MetaDictionary {
41            scheme_uri: scheme_uri.into(),
42            scheme_id: scheme_id.into(),
43            ..Default::default()
44        }
45    }
46
47    // ── fromXML ─────────────────────────────────────────────────────────────
48
49    pub fn from_xml(xml: &[u8]) -> Result<Self, DictError> {
50        let text = std::str::from_utf8(xml).map_err(|e| DictError::Xml(e.to_string()))?;
51        let doc = roxmltree::Document::parse(text).map_err(|e| DictError::Xml(e.to_string()))?;
52        let root = doc.root_element(); // <Extension>
53
54        let scheme_id = child_text(&root, "SchemeID").unwrap_or_default();
55        let scheme_uri = child_text(&root, "SchemeURI").unwrap_or_default();
56        let mut dict = MetaDictionary::new(scheme_uri, scheme_id);
57
58        if let Some(meta_defs) = root
59            .children()
60            .find(|n| n.tag_name().name() == "MetaDefinitions")
61        {
62            for node in meta_defs.children().filter(|n| n.is_element()) {
63                if let Some(def) = parse_meta_definition(&node)
64                    .map_err(|e| DictError::Xml(format!("in <{}>: {e}", node.tag_name().name())))?
65                {
66                    dict.add(def)?;
67                }
68            }
69        }
70        Ok(dict)
71    }
72
73    // ── add ──────────────────────────────────────────────────────────────────
74
75    pub fn add(&mut self, def: Definition) -> Result<(), DictError> {
76        let id = *def.identification();
77        let sym = def.symbol().to_owned();
78
79        // Build reverse-lookup indexes before inserting
80        match &def {
81            Definition::Property(p) => {
82                self.members_of.entry(p.member_of).or_default().push(id);
83            }
84            Definition::Class(c) => {
85                if let Some(parent) = c.parent_class {
86                    self.subclasses_of.entry(parent).or_default().push(id);
87                }
88            }
89            Definition::Type(_) => {}
90        }
91
92        self.definitions_by_symbol.insert(sym, id);
93        self.definitions_by_auid.insert(id, def);
94        Ok(())
95    }
96
97    // ── accessors ─────────────────────────────────────────────────────────────
98
99    pub fn definition_count(&self) -> usize {
100        self.definitions_by_auid.len()
101    }
102
103    pub fn all_definitions(&self) -> impl Iterator<Item = &Definition> {
104        self.definitions_by_auid.values()
105    }
106
107    pub fn get_definition_by_symbol(&self, symbol: &str) -> Option<&Definition> {
108        self.definitions_by_symbol
109            .get(symbol)
110            .and_then(|id| self.definitions_by_auid.get(id))
111    }
112
113    // ── to_xml ────────────────────────────────────────────────────────────────
114
115    pub fn to_xml(&self) -> Result<Vec<u8>, DictError> {
116        let mut buf = Vec::new();
117        let mut w = Writer::new_with_indent(&mut buf, b' ', 2);
118
119        w.write_event(Event::Decl(BytesDecl::new("1.0", Some("UTF-8"), None)))
120            .map_err(|e| DictError::Xml(e.to_string()))?;
121
122        let mut root = BytesStart::new("Extension");
123        root.push_attribute(("xmlns", NS));
124        w.write_event(Event::Start(root))
125            .map_err(|e| DictError::Xml(e.to_string()))?;
126
127        write_simple(&mut w, "SchemeID", &self.scheme_id)?;
128        write_simple(&mut w, "SchemeURI", &self.scheme_uri)?;
129
130        w.write_event(Event::Start(BytesStart::new("MetaDefinitions")))
131            .map_err(|e| DictError::Xml(e.to_string()))?;
132
133        for def in self.definitions_by_auid.values() {
134            write_definition(&mut w, def)?;
135        }
136
137        w.write_event(Event::End(BytesEnd::new("MetaDefinitions")))
138            .map_err(|e| DictError::Xml(e.to_string()))?;
139        w.write_event(Event::End(BytesEnd::new("Extension")))
140            .map_err(|e| DictError::Xml(e.to_string()))?;
141
142        Ok(buf)
143    }
144}
145
146impl DefinitionResolver for MetaDictionary {
147    fn get_definition(&self, id: &Auid) -> Option<&Definition> {
148        self.definitions_by_auid.get(id)
149    }
150    fn get_subclasses_of(&self, class: &ClassDefinition) -> Vec<Auid> {
151        self.subclasses_of
152            .get(&class.identification)
153            .cloned()
154            .unwrap_or_default()
155    }
156    fn get_members_of(&self, class: &ClassDefinition) -> Vec<Auid> {
157        self.members_of
158            .get(&class.identification)
159            .cloned()
160            .unwrap_or_default()
161    }
162}
163
164// ── MetaDictionaryCollection ─────────────────────────────────────────────────
165
166/// Aggregates multiple [`MetaDictionary`] instances with a merged lookup index.
167#[derive(Debug, Default)]
168pub struct MetaDictionaryCollection {
169    dictionaries: Vec<MetaDictionary>,
170    index: HashMap<Auid, usize>,
171}
172
173impl MetaDictionaryCollection {
174    pub fn new() -> Self {
175        Self::default()
176    }
177
178    pub fn add(&mut self, dict: MetaDictionary) -> Result<(), DictError> {
179        let idx = self.dictionaries.len();
180        for id in dict.definitions_by_auid.keys() {
181            self.index.entry(*id).or_insert(idx);
182        }
183        self.dictionaries.push(dict);
184        Ok(())
185    }
186
187    pub fn from_xml_slices(slices: &[&[u8]]) -> Result<Self, DictError> {
188        let mut coll = Self::new();
189        for xml in slices {
190            coll.add(MetaDictionary::from_xml(xml)?)?;
191        }
192        Ok(coll)
193    }
194
195    pub fn get_definition_by_symbol(&self, symbol: &str) -> Option<&Definition> {
196        self.dictionaries
197            .iter()
198            .find_map(|d| d.get_definition_by_symbol(symbol))
199    }
200}
201
202impl DefinitionResolver for MetaDictionaryCollection {
203    fn get_definition(&self, id: &Auid) -> Option<&Definition> {
204        self.index
205            .get(id)
206            .and_then(|&i| self.dictionaries.get(i))
207            .and_then(|d| d.definitions_by_auid.get(id))
208    }
209    fn get_subclasses_of(&self, class: &ClassDefinition) -> Vec<Auid> {
210        self.dictionaries
211            .iter()
212            .flat_map(|d| d.get_subclasses_of(class))
213            .collect()
214    }
215    fn get_members_of(&self, class: &ClassDefinition) -> Vec<Auid> {
216        self.dictionaries
217            .iter()
218            .flat_map(|d| d.get_members_of(class))
219            .collect()
220    }
221}
222
223// ── Parsing helpers ───────────────────────────────────────────────────────────
224
225fn child_text(node: &roxmltree::Node<'_, '_>, name: &str) -> Option<String> {
226    node.children()
227        .find(|n| n.tag_name().name() == name)
228        .and_then(|n| n.text())
229        .map(str::to_owned)
230}
231
232fn required_auid(
233    node: &roxmltree::Node<'_, '_>,
234    field: &'static str,
235    ctx: &str,
236) -> Result<Auid, DictError> {
237    let s =
238        child_text(node, field).ok_or_else(|| DictError::MissingField(field, ctx.to_owned()))?;
239    Auid::from_urn(&s).map_err(|e| DictError::Xml(format!("bad AUID in <{field}> of {ctx}: {e}")))
240}
241
242fn optional_auid(node: &roxmltree::Node<'_, '_>, field: &str) -> Result<Option<Auid>, DictError> {
243    if let Some(s) = child_text(node, field) {
244        Ok(Some(Auid::from_urn(&s).map_err(|e| {
245            DictError::Xml(format!("bad AUID in <{field}>: {e}"))
246        })?))
247    } else {
248        Ok(None)
249    }
250}
251
252fn required_str(
253    node: &roxmltree::Node<'_, '_>,
254    field: &'static str,
255    ctx: &str,
256) -> Result<String, DictError> {
257    child_text(node, field).ok_or_else(|| DictError::MissingField(field, ctx.to_owned()))
258}
259
260fn parse_bool(s: &str) -> bool {
261    s.eq_ignore_ascii_case("true")
262}
263
264// ── Per-element-name parsers ─────────────────────────────────────────────────
265
266fn parse_meta_definition(node: &roxmltree::Node<'_, '_>) -> Result<Option<Definition>, DictError> {
267    let tag = node.tag_name().name();
268    let id_str = match child_text(node, "Identification") {
269        Some(s) => s,
270        None => return Ok(None), // ignore entries without Identification
271    };
272    let ctx = format!("{tag}:{id_str}");
273
274    let ident = Auid::from_urn(&id_str).map_err(|e| DictError::Xml(format!("{ctx}: {e}")))?;
275    let symbol = required_str(node, "Symbol", &ctx)?;
276    let _name = child_text(node, "Name").unwrap_or_default();
277
278    match tag {
279        "ClassDefinition" => {
280            let parent_class = optional_auid(node, "ParentClass")?;
281            let is_concrete = child_text(node, "IsConcrete")
282                .as_deref()
283                .map(parse_bool)
284                .unwrap_or(true);
285            Ok(Some(Definition::Class(ClassDefinition {
286                identification: ident,
287                symbol,
288                namespace: String::new(),
289                parent_class,
290                is_concrete,
291            })))
292        }
293
294        "PropertyDefinition" => {
295            let prop_type = required_auid(node, "Type", &ctx)?;
296            let member_of = required_auid(node, "MemberOf", &ctx)?;
297            let is_optional = child_text(node, "IsOptional")
298                .as_deref()
299                .map(parse_bool)
300                .unwrap_or(false);
301            let is_unique = child_text(node, "IsUniqueIdentifier")
302                .as_deref()
303                .map(parse_bool)
304                .unwrap_or(false);
305            let local_id: u16 = child_text(node, "LocalIdentification")
306                .as_deref()
307                .and_then(|s| s.parse().ok())
308                .unwrap_or(0);
309            Ok(Some(Definition::Property(PropertyDefinition {
310                identification: ident,
311                symbol,
312                namespace: String::new(),
313                member_of,
314                property_type: prop_type,
315                is_optional,
316                is_unique_identifier: is_unique,
317                local_identification: local_id,
318            })))
319        }
320
321        "TypeDefinitionInteger" => {
322            let size = child_text(node, "Size")
323                .and_then(|s| s.parse::<u8>().ok())
324                .unwrap_or(1);
325            let is_signed = child_text(node, "IsSigned")
326                .as_deref()
327                .map(parse_bool)
328                .unwrap_or(false);
329            Ok(Some(Definition::Type(TypeDefinition::Integer(
330                IntegerTypeDef {
331                    identification: ident,
332                    symbol,
333                    namespace: String::new(),
334                    size,
335                    is_signed,
336                },
337            ))))
338        }
339
340        "TypeDefinitionRename" => {
341            let renamed = required_auid(node, "RenamedType", &ctx)?;
342            Ok(Some(Definition::Type(TypeDefinition::Rename(
343                RenameTypeDef {
344                    identification: ident,
345                    symbol,
346                    namespace: String::new(),
347                    renamed_type: renamed,
348                },
349            ))))
350        }
351
352        "TypeDefinitionRecord" => {
353            let members = parse_record_members(node)?;
354            Ok(Some(Definition::Type(TypeDefinition::Record(
355                RecordTypeDef {
356                    identification: ident,
357                    symbol,
358                    namespace: String::new(),
359                    members,
360                },
361            ))))
362        }
363
364        "TypeDefinitionEnumeration" => {
365            let element_type = required_auid(node, "ElementType", &ctx)?;
366            let elements = parse_enum_elements(node)?;
367            Ok(Some(Definition::Type(TypeDefinition::Enumeration(
368                EnumerationTypeDef {
369                    identification: ident,
370                    symbol,
371                    namespace: String::new(),
372                    element_type,
373                    elements,
374                },
375            ))))
376        }
377
378        "TypeDefinitionExtendibleEnumeration" => {
379            let elements = parse_enum_elements(node)?;
380            Ok(Some(Definition::Type(
381                TypeDefinition::ExtendibleEnumeration(ExtEnumTypeDef {
382                    identification: ident,
383                    symbol,
384                    namespace: String::new(),
385                    elements,
386                }),
387            )))
388        }
389
390        "TypeDefinitionFixedArray" => {
391            let element_type = required_auid(node, "ElementType", &ctx)?;
392            let element_count = child_text(node, "ElementCount")
393                .and_then(|s| s.parse::<u32>().ok())
394                .unwrap_or(0);
395            Ok(Some(Definition::Type(TypeDefinition::FixedArray(
396                FixedArrayTypeDef {
397                    identification: ident,
398                    symbol,
399                    namespace: String::new(),
400                    element_type,
401                    element_count,
402                },
403            ))))
404        }
405
406        "TypeDefinitionVariableArray" => {
407            let element_type = required_auid(node, "ElementType", &ctx)?;
408            Ok(Some(Definition::Type(TypeDefinition::VariableArray(
409                VariableArrayTypeDef {
410                    identification: ident,
411                    symbol,
412                    namespace: String::new(),
413                    element_type,
414                },
415            ))))
416        }
417
418        "TypeDefinitionSet" => {
419            let element_type = required_auid(node, "ElementType", &ctx)?;
420            Ok(Some(Definition::Type(TypeDefinition::Set(SetTypeDef {
421                identification: ident,
422                symbol,
423                namespace: String::new(),
424                element_type,
425            }))))
426        }
427
428        "TypeDefinitionString" => {
429            let element_type = required_auid(node, "ElementType", &ctx)?;
430            Ok(Some(Definition::Type(TypeDefinition::String(
431                StringTypeDef {
432                    identification: ident,
433                    symbol,
434                    namespace: String::new(),
435                    element_type,
436                },
437            ))))
438        }
439
440        "TypeDefinitionCharacter" => Ok(Some(Definition::Type(TypeDefinition::Character(
441            CharacterTypeDef {
442                identification: ident,
443                symbol,
444                namespace: String::new(),
445            },
446        )))),
447
448        "TypeDefinitionStream" => Ok(Some(Definition::Type(TypeDefinition::Stream(
449            StreamTypeDef {
450                identification: ident,
451                symbol,
452                namespace: String::new(),
453            },
454        )))),
455
456        "TypeDefinitionIndirect" => Ok(Some(Definition::Type(TypeDefinition::Indirect(
457            IndirectTypeDef {
458                identification: ident,
459                symbol,
460                namespace: String::new(),
461            },
462        )))),
463
464        "TypeDefinitionOpaque" => Ok(Some(Definition::Type(TypeDefinition::Opaque(
465            OpaqueTypeDef {
466                identification: ident,
467                symbol,
468                namespace: String::new(),
469            },
470        )))),
471
472        "TypeDefinitionStrongObjectReference" => {
473            let referenced = required_auid(node, "ReferencedType", &ctx)?;
474            Ok(Some(Definition::Type(TypeDefinition::StrongReference(
475                StrongReferenceTypeDef {
476                    identification: ident,
477                    symbol,
478                    namespace: String::new(),
479                    referenced_type: referenced,
480                },
481            ))))
482        }
483
484        "TypeDefinitionWeakObjectReference" => {
485            let referenced = required_auid(node, "ReferencedType", &ctx)?;
486            let target_set = parse_target_set(node)?;
487            Ok(Some(Definition::Type(TypeDefinition::WeakReference(
488                WeakReferenceTypeDef {
489                    identification: ident,
490                    symbol,
491                    namespace: String::new(),
492                    referenced_type: referenced,
493                    target_set,
494                },
495            ))))
496        }
497
498        // Float is not in the standard dict files but may appear in extensions
499        "TypeDefinitionFloat" => {
500            let size = child_text(node, "Size")
501                .and_then(|s| s.parse::<u8>().ok())
502                .unwrap_or(4);
503            Ok(Some(Definition::Type(TypeDefinition::Float(
504                FloatTypeDef {
505                    identification: ident,
506                    symbol,
507                    namespace: String::new(),
508                    size,
509                },
510            ))))
511        }
512
513        "TypeDefinitionLensSerialFloat" => Ok(Some(Definition::Type(
514            TypeDefinition::LensSerialFloat(LensSerialFloatTypeDef {
515                identification: ident,
516                symbol,
517                namespace: String::new(),
518            }),
519        ))),
520
521        _ => Ok(None), // unknown — skip gracefully
522    }
523}
524
525/// Parse `<Members>` as alternating `<Name>` / `<Type>` children.
526fn parse_record_members(node: &roxmltree::Node<'_, '_>) -> Result<Vec<RecordMember>, DictError> {
527    let members_node = match node.children().find(|n| n.tag_name().name() == "Members") {
528        Some(n) => n,
529        None => return Ok(vec![]),
530    };
531
532    let mut names: Vec<String> = Vec::new();
533    let mut types: Vec<String> = Vec::new();
534    for child in members_node.children().filter(|n| n.is_element()) {
535        match child.tag_name().name() {
536            "Name" => names.push(child.text().unwrap_or_default().to_owned()),
537            "Type" => types.push(child.text().unwrap_or_default().to_owned()),
538            _ => {}
539        }
540    }
541    names
542        .into_iter()
543        .zip(types)
544        .map(|(n, t)| {
545            let field_type = Auid::from_urn(&t)
546                .map_err(|e| DictError::Xml(format!("bad member type {t:?}: {e}")))?;
547            Ok(RecordMember {
548                name: n,
549                field_type,
550            })
551        })
552        .collect()
553}
554
555/// Parse `<Elements>` as alternating `<Name>` / `<Value>` children.
556fn parse_enum_elements(
557    node: &roxmltree::Node<'_, '_>,
558) -> Result<Vec<EnumerationElement>, DictError> {
559    let elems_node = match node.children().find(|n| n.tag_name().name() == "Elements") {
560        Some(n) => n,
561        None => return Ok(vec![]),
562    };
563
564    let mut names: Vec<String> = Vec::new();
565    let mut values: Vec<i64> = Vec::new();
566    for child in elems_node.children().filter(|n| n.is_element()) {
567        match child.tag_name().name() {
568            "Name" => names.push(child.text().unwrap_or_default().to_owned()),
569            "Value" => {
570                let v = child
571                    .text()
572                    .unwrap_or("0")
573                    .trim()
574                    .parse::<i64>()
575                    .unwrap_or(0);
576                values.push(v);
577            }
578            _ => {}
579        }
580    }
581    Ok(names
582        .into_iter()
583        .zip(values)
584        .map(|(name, value)| EnumerationElement { name, value })
585        .collect())
586}
587
588/// Parse `<TargetSet><MetaDefRef>…</MetaDefRef>…</TargetSet>`.
589fn parse_target_set(node: &roxmltree::Node<'_, '_>) -> Result<Vec<Auid>, DictError> {
590    let ts = match node.children().find(|n| n.tag_name().name() == "TargetSet") {
591        Some(n) => n,
592        None => return Ok(vec![]),
593    };
594    ts.children()
595        .filter(|n| n.tag_name().name() == "MetaDefRef")
596        .filter_map(|n| n.text())
597        .map(|s| Auid::from_urn(s).map_err(|e| DictError::Xml(e.to_string())))
598        .collect()
599}
600
601// ── XML serialisation helpers ─────────────────────────────────────────────────
602
603fn write_simple<W: std::io::Write>(
604    w: &mut Writer<W>,
605    tag: &str,
606    text: &str,
607) -> Result<(), DictError> {
608    w.write_event(Event::Start(BytesStart::new(tag)))
609        .and_then(|_| w.write_event(Event::Text(BytesText::new(text))))
610        .and_then(|_| w.write_event(Event::End(BytesEnd::new(tag))))
611        .map_err(|e| DictError::Xml(e.to_string()))
612}
613
614fn write_definition<W: std::io::Write>(
615    w: &mut Writer<W>,
616    def: &Definition,
617) -> Result<(), DictError> {
618    match def {
619        Definition::Class(c) => write_class(w, c),
620        Definition::Property(p) => write_property(w, p),
621        Definition::Type(t) => write_type(w, t),
622    }
623}
624
625fn write_class<W: std::io::Write>(w: &mut Writer<W>, c: &ClassDefinition) -> Result<(), DictError> {
626    w.write_event(Event::Start(BytesStart::new("ClassDefinition")))
627        .map_err(|e| DictError::Xml(e.to_string()))?;
628    write_simple(w, "Identification", &c.identification.to_string())?;
629    write_simple(w, "Symbol", &c.symbol)?;
630    write_simple(w, "Name", "")?;
631    if let Some(p) = c.parent_class {
632        write_simple(w, "ParentClass", &p.to_string())?;
633    }
634    write_simple(
635        w,
636        "IsConcrete",
637        if c.is_concrete { "true" } else { "false" },
638    )?;
639    w.write_event(Event::End(BytesEnd::new("ClassDefinition")))
640        .map_err(|e| DictError::Xml(e.to_string()))
641}
642
643fn write_property<W: std::io::Write>(
644    w: &mut Writer<W>,
645    p: &PropertyDefinition,
646) -> Result<(), DictError> {
647    w.write_event(Event::Start(BytesStart::new("PropertyDefinition")))
648        .map_err(|e| DictError::Xml(e.to_string()))?;
649    write_simple(w, "Identification", &p.identification.to_string())?;
650    write_simple(w, "Symbol", &p.symbol)?;
651    write_simple(w, "Name", "")?;
652    write_simple(w, "Type", &p.property_type.to_string())?;
653    write_simple(
654        w,
655        "IsOptional",
656        if p.is_optional { "true" } else { "false" },
657    )?;
658    write_simple(
659        w,
660        "IsUniqueIdentifier",
661        if p.is_unique_identifier {
662            "true"
663        } else {
664            "false"
665        },
666    )?;
667    write_simple(
668        w,
669        "LocalIdentification",
670        &p.local_identification.to_string(),
671    )?;
672    write_simple(w, "MemberOf", &p.member_of.to_string())?;
673    w.write_event(Event::End(BytesEnd::new("PropertyDefinition")))
674        .map_err(|e| DictError::Xml(e.to_string()))
675}
676
677fn write_type<W: std::io::Write>(w: &mut Writer<W>, t: &TypeDefinition) -> Result<(), DictError> {
678    let (tag, id, sym): (&str, &Auid, &str) = match t {
679        TypeDefinition::Integer(d) => ("TypeDefinitionInteger", &d.identification, &d.symbol),
680        TypeDefinition::Rename(d) => ("TypeDefinitionRename", &d.identification, &d.symbol),
681        TypeDefinition::Record(d) => ("TypeDefinitionRecord", &d.identification, &d.symbol),
682        TypeDefinition::Enumeration(d) => {
683            ("TypeDefinitionEnumeration", &d.identification, &d.symbol)
684        }
685        TypeDefinition::ExtendibleEnumeration(d) => (
686            "TypeDefinitionExtendibleEnumeration",
687            &d.identification,
688            &d.symbol,
689        ),
690        TypeDefinition::FixedArray(d) => ("TypeDefinitionFixedArray", &d.identification, &d.symbol),
691        TypeDefinition::VariableArray(d) => {
692            ("TypeDefinitionVariableArray", &d.identification, &d.symbol)
693        }
694        TypeDefinition::Set(d) => ("TypeDefinitionSet", &d.identification, &d.symbol),
695        TypeDefinition::String(d) => ("TypeDefinitionString", &d.identification, &d.symbol),
696        TypeDefinition::Character(d) => ("TypeDefinitionCharacter", &d.identification, &d.symbol),
697        TypeDefinition::Stream(d) => ("TypeDefinitionStream", &d.identification, &d.symbol),
698        TypeDefinition::Indirect(d) => ("TypeDefinitionIndirect", &d.identification, &d.symbol),
699        TypeDefinition::Opaque(d) => ("TypeDefinitionOpaque", &d.identification, &d.symbol),
700        TypeDefinition::StrongReference(d) => (
701            "TypeDefinitionStrongObjectReference",
702            &d.identification,
703            &d.symbol,
704        ),
705        TypeDefinition::WeakReference(d) => (
706            "TypeDefinitionWeakObjectReference",
707            &d.identification,
708            &d.symbol,
709        ),
710        TypeDefinition::Float(d) => ("TypeDefinitionFloat", &d.identification, &d.symbol),
711        TypeDefinition::LensSerialFloat(d) => (
712            "TypeDefinitionLensSerialFloat",
713            &d.identification,
714            &d.symbol,
715        ),
716    };
717
718    w.write_event(Event::Start(BytesStart::new(tag)))
719        .map_err(|e| DictError::Xml(e.to_string()))?;
720    write_simple(w, "Identification", &id.to_string())?;
721    write_simple(w, "Symbol", sym)?;
722
723    // type-specific extra fields
724    match t {
725        TypeDefinition::Integer(d) => {
726            write_simple(w, "Size", &d.size.to_string())?;
727            write_simple(w, "IsSigned", if d.is_signed { "true" } else { "false" })?;
728        }
729        TypeDefinition::Rename(d) => {
730            write_simple(w, "RenamedType", &d.renamed_type.to_string())?;
731        }
732        TypeDefinition::Record(d) => {
733            w.write_event(Event::Start(BytesStart::new("Members")))
734                .map_err(|e| DictError::Xml(e.to_string()))?;
735            for m in &d.members {
736                write_simple(w, "Name", &m.name)?;
737                write_simple(w, "Type", &m.field_type.to_string())?;
738            }
739            w.write_event(Event::End(BytesEnd::new("Members")))
740                .map_err(|e| DictError::Xml(e.to_string()))?;
741        }
742        TypeDefinition::Enumeration(d) => {
743            write_simple(w, "ElementType", &d.element_type.to_string())?;
744            w.write_event(Event::Start(BytesStart::new("Elements")))
745                .map_err(|e| DictError::Xml(e.to_string()))?;
746            for e in &d.elements {
747                write_simple(w, "Name", &e.name)?;
748                write_simple(w, "Value", &e.value.to_string())?;
749            }
750            w.write_event(Event::End(BytesEnd::new("Elements")))
751                .map_err(|e| DictError::Xml(e.to_string()))?;
752        }
753        TypeDefinition::ExtendibleEnumeration(d) => {
754            w.write_event(Event::Start(BytesStart::new("Elements")))
755                .map_err(|e| DictError::Xml(e.to_string()))?;
756            for e in &d.elements {
757                write_simple(w, "Name", &e.name)?;
758                write_simple(w, "Value", &e.value.to_string())?;
759            }
760            w.write_event(Event::End(BytesEnd::new("Elements")))
761                .map_err(|e| DictError::Xml(e.to_string()))?;
762        }
763        TypeDefinition::FixedArray(d) => {
764            write_simple(w, "ElementCount", &d.element_count.to_string())?;
765            write_simple(w, "ElementType", &d.element_type.to_string())?;
766        }
767        TypeDefinition::VariableArray(d) => {
768            write_simple(w, "ElementType", &d.element_type.to_string())?;
769        }
770        TypeDefinition::Set(d) => {
771            write_simple(w, "ElementType", &d.element_type.to_string())?;
772        }
773        TypeDefinition::String(d) => {
774            write_simple(w, "ElementType", &d.element_type.to_string())?;
775        }
776        TypeDefinition::StrongReference(d) => {
777            write_simple(w, "ReferencedType", &d.referenced_type.to_string())?;
778        }
779        TypeDefinition::WeakReference(d) => {
780            write_simple(w, "ReferencedType", &d.referenced_type.to_string())?;
781            w.write_event(Event::Start(BytesStart::new("TargetSet")))
782                .map_err(|e| DictError::Xml(e.to_string()))?;
783            for ts in &d.target_set {
784                write_simple(w, "MetaDefRef", &ts.to_string())?;
785            }
786            w.write_event(Event::End(BytesEnd::new("TargetSet")))
787                .map_err(|e| DictError::Xml(e.to_string()))?;
788        }
789        TypeDefinition::Float(d) => {
790            write_simple(w, "Size", &d.size.to_string())?;
791        }
792        _ => {} // no extra fields for Character, Stream, Indirect, Opaque, LensSerialFloat
793    }
794
795    w.write_event(Event::End(BytesEnd::new(tag)))
796        .map_err(|e| DictError::Xml(e.to_string()))
797}