libgir/
parser.rs

1use std::{
2    path::{Path, PathBuf},
3    str::FromStr,
4};
5
6use log::{trace, warn};
7
8use crate::{
9    library::*,
10    version::Version,
11    xmlparser::{Element, XmlParser},
12};
13
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
15pub enum DocFormat {
16    GtkDocMarkdown,
17    GtkDocDocbook,
18    GiDocgen,
19    Hotdoc,
20    #[default]
21    Unknown,
22}
23
24impl std::str::FromStr for DocFormat {
25    type Err = String;
26
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        match s {
29            "gtk-doc-markdown" => Ok(Self::GtkDocMarkdown),
30            "gtk-doc-docbook" => Ok(Self::GtkDocDocbook),
31            "gi-docgen" => Ok(Self::GiDocgen),
32            "hotdoc" => Ok(Self::Hotdoc),
33            "unknown" => Ok(Self::Unknown),
34            e => Err(format!("Invalid doc:format {e}")),
35        }
36    }
37}
38
39const EMPTY_CTYPE: &str = "/*EMPTY*/";
40
41pub fn is_empty_c_type(c_type: &str) -> bool {
42    c_type == EMPTY_CTYPE
43}
44
45impl Library {
46    pub fn read_file<P: AsRef<Path>>(
47        &mut self,
48        dirs: &[P],
49        libs: &mut Vec<String>,
50    ) -> Result<(), String> {
51        for dir in dirs {
52            let dir: &Path = dir.as_ref();
53            let file_name = make_file_name(dir, &libs[libs.len() - 1]);
54            let Ok(mut parser) = XmlParser::from_path(&file_name) else {
55                continue;
56            };
57            return parser.document(|p, _| {
58                p.element_with_name("repository", |sub_parser, _elem| {
59                    self.read_repository(dirs, sub_parser, libs)
60                })
61            });
62        }
63        Err(format!("Couldn't find `{}`...", &libs[libs.len() - 1]))
64    }
65
66    fn read_repository<P: AsRef<Path>>(
67        &mut self,
68        dirs: &[P],
69        parser: &mut XmlParser<'_>,
70        libs: &mut Vec<String>,
71    ) -> Result<(), String> {
72        let mut packages = Vec::new();
73        let mut includes = Vec::new();
74        parser.elements(|parser, elem| match elem.name() {
75            "include" => {
76                match (elem.attr("name"), elem.attr("version")) {
77                    (Some(name), Some(ver)) => {
78                        if self.find_namespace(name).is_none() {
79                            let lib = format!("{name}-{ver}");
80                            if libs.contains(&lib) {
81                                return Err(format!(
82                                    "`{}` includes itself (full path:`{}`)!",
83                                    lib,
84                                    libs.join("::")
85                                ));
86                            }
87                            libs.push(lib);
88                            self.read_file(dirs, libs)?;
89                            libs.pop();
90                        }
91                    }
92                    (Some(name), None) => includes.push(name.to_owned()),
93                    _ => {}
94                }
95                Ok(())
96            }
97            "package" => {
98                let name = elem.attr_required("name")?;
99                packages.push(name.to_owned());
100                Ok(())
101            }
102            "namespace" => self.read_namespace(
103                parser,
104                elem,
105                std::mem::take(&mut packages),
106                std::mem::take(&mut includes),
107            ),
108            "format" => {
109                let format = DocFormat::from_str(elem.attr_required("name")?)?;
110                self.doc_format = format;
111                Ok(())
112            }
113            "attribute" => parser.ignore_element(),
114            _ => Err(parser.unexpected_element(elem)),
115        })?;
116        Ok(())
117    }
118
119    fn read_namespace(
120        &mut self,
121        parser: &mut XmlParser<'_>,
122        elem: &Element,
123        packages: Vec<String>,
124        c_includes: Vec<String>,
125    ) -> Result<(), String> {
126        let ns_name = elem.attr_required("name")?;
127        let ns_id = self.add_namespace(ns_name);
128
129        {
130            let ns = self.namespace_mut(ns_id);
131            ns.package_names = packages;
132            ns.c_includes = c_includes;
133            if let Some(s) = elem.attr("shared-library") {
134                ns.shared_library = s
135                    .split(',')
136                    .filter_map(|x| {
137                        if !x.is_empty() {
138                            Some(String::from(x))
139                        } else {
140                            None
141                        }
142                    })
143                    .collect();
144            }
145            if let Some(s) = elem.attr("identifier-prefixes") {
146                ns.identifier_prefixes = s.split(',').map(String::from).collect();
147            }
148            if let Some(s) = elem.attr("symbol-prefixes") {
149                ns.symbol_prefixes = s.split(',').map(String::from).collect();
150            }
151        }
152
153        trace!(
154            "Reading {}-{}",
155            ns_name,
156            elem.attr("version").unwrap_or("?")
157        );
158
159        parser.elements(|parser, elem| {
160            trace!("<{} name={:?}>", elem.name(), elem.attr("name"));
161            match elem.name() {
162                "class" => self.read_class(parser, ns_id, elem),
163                "record" => self.read_record_start(parser, ns_id, elem),
164                "union" => self.read_named_union(parser, ns_id, elem),
165                "interface" => self.read_interface(parser, ns_id, elem),
166                "callback" => self.read_named_callback(parser, ns_id, elem),
167                "bitfield" => self.read_bitfield(parser, ns_id, elem),
168                "enumeration" => self.read_enumeration(parser, ns_id, elem),
169                "function" => self.read_global_function(parser, ns_id, elem),
170                "constant" => self.read_constant(parser, ns_id, elem),
171                "alias" => self.read_alias(parser, ns_id, elem),
172                "boxed" | "function-macro" | "docsection" | "function-inline" => {
173                    parser.ignore_element()
174                }
175                _ => {
176                    warn!("<{} name={:?}>", elem.name(), elem.attr("name"));
177                    parser.ignore_element()
178                }
179            }
180        })?;
181        Ok(())
182    }
183
184    fn read_class(
185        &mut self,
186        parser: &mut XmlParser<'_>,
187        ns_id: u16,
188        elem: &Element,
189    ) -> Result<(), String> {
190        let class_name = elem.attr_required("name")?;
191        let c_type = self.read_object_c_type(parser, elem)?;
192        let symbol_prefix = elem.attr_required("symbol-prefix").map(ToOwned::to_owned)?;
193        let type_struct = elem.attr("type-struct").map(ToOwned::to_owned);
194        let get_type = elem.attr_required("get-type")?;
195        let version = self.read_version(parser, ns_id, elem)?;
196        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
197        let is_fundamental = elem.attr("fundamental").is_some_and(|x| x == "1");
198        let (ref_fn, unref_fn) = if is_fundamental {
199            (
200                elem.attr("ref-func").map(ToOwned::to_owned),
201                elem.attr("unref-func").map(ToOwned::to_owned),
202            )
203        } else {
204            (None, None)
205        };
206
207        let is_abstract = elem.attr("abstract").is_some_and(|x| x == "1");
208        let final_type = elem.attr("final").is_some_and(|x| x == "1");
209
210        let mut fns = Vec::new();
211        let mut signals = Vec::new();
212        let mut properties = Vec::new();
213        let mut impls = Vec::new();
214        let mut fields = Vec::new();
215        let mut vfns = Vec::new();
216        let mut doc = None;
217        let mut doc_deprecated = None;
218        let mut union_count = 1;
219
220        parser.elements(|parser, elem| match elem.name() {
221            "constructor" | "function" | "method" => {
222                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
223            }
224            "implements" => self.read_type(parser, ns_id, elem).map(|r| {
225                impls.push(r.0);
226            }),
227            "signal" => self
228                .read_signal(parser, ns_id, elem)
229                .map(|s| signals.push(s)),
230            "property" => self
231                .read_property(parser, ns_id, elem, &symbol_prefix)
232                .map(|p| {
233                    if let Some(p) = p {
234                        properties.push(p);
235                    }
236                }),
237            "field" => self.read_field(parser, ns_id, elem).map(|f| {
238                fields.push(f);
239            }),
240            "virtual-method" => self
241                .read_virtual_method(parser, ns_id, elem)
242                .map(|v| vfns.push(v)),
243            "doc" => parser.text().map(|t| doc = Some(t)),
244            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
245            "doc-version" => parser.ignore_element(),
246            "source-position" => parser.ignore_element(),
247            "union" => self
248                .read_union(parser, ns_id, elem, Some(class_name), Some(c_type))
249                .map(|mut u| {
250                    let field_name = if let Some(field_name) = elem.attr("name") {
251                        field_name.into()
252                    } else {
253                        format!("u{union_count}")
254                    };
255
256                    u = Union {
257                        name: format!("{class_name}_{field_name}"),
258                        c_type: Some(format!("{c_type}_{field_name}")),
259                        ..u
260                    };
261
262                    let u_doc = u.doc.clone();
263                    let ctype = u.c_type.clone();
264
265                    fields.push(Field {
266                        name: field_name,
267                        typ: Type::union(self, u, ns_id),
268                        doc: u_doc,
269                        c_type: ctype,
270                        ..Field::default()
271                    });
272                    union_count += 1;
273                }),
274            "attribute" => parser.ignore_element(),
275            _ => Err(parser.unexpected_element(elem)),
276        })?;
277
278        let parent = elem
279            .attr("parent")
280            .map(|s| self.find_or_stub_type(ns_id, s));
281        let typ = Type::Class(Class {
282            name: class_name.into(),
283            c_type: c_type.into(),
284            type_struct,
285            c_class_type: None, // this will be resolved during postprocessing
286            glib_get_type: get_type.into(),
287            fields,
288            functions: fns,
289            virtual_methods: vfns,
290            signals,
291            properties,
292            parent,
293            implements: impls,
294            final_type,
295            doc,
296            doc_deprecated,
297            version,
298            deprecated_version,
299            symbol_prefix,
300            is_abstract,
301            is_fundamental,
302            ref_fn,
303            unref_fn,
304        });
305        self.add_type(ns_id, class_name, typ);
306        Ok(())
307    }
308
309    fn read_record_start(
310        &mut self,
311        parser: &mut XmlParser<'_>,
312        ns_id: u16,
313        elem: &Element,
314    ) -> Result<(), String> {
315        if let Some(typ) = self.read_record(parser, ns_id, elem, None, None)? {
316            let name = typ.get_name();
317            self.add_type(ns_id, &name, typ);
318        }
319        Ok(())
320    }
321
322    fn read_record(
323        &mut self,
324        parser: &mut XmlParser<'_>,
325        ns_id: u16,
326        elem: &Element,
327        parent_name_prefix: Option<&str>,
328        parent_ctype_prefix: Option<&str>,
329    ) -> Result<Option<Type>, String> {
330        let record_name = elem.attr("name").unwrap_or_default();
331        // Records starting with `_` are intended to be private and should not be bound
332        if record_name.starts_with('_') {
333            parser.ignore_element()?;
334            return Ok(None);
335        }
336        let is_class_record = record_name.ends_with("Class");
337
338        let c_type = elem.attr("type").unwrap_or_default();
339        let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned);
340        let get_type = elem.attr("get-type").map(ToOwned::to_owned);
341        let gtype_struct_for = elem.attr("is-gtype-struct-for");
342        let version = self.read_version(parser, ns_id, elem)?;
343        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
344        let pointer = elem.attr_bool("pointer", false);
345        let disguised = elem.attr_bool("disguised", false);
346
347        let mut fields = Vec::new();
348        let mut fns = Vec::new();
349        let mut doc = None;
350        let mut doc_deprecated = None;
351        let mut union_count = 1;
352
353        parser.elements(|parser, elem| match elem.name() {
354            "constructor" | "function" | "method" => {
355                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
356            }
357            "union" => self
358                .read_union(parser, ns_id, elem, Some(record_name), Some(c_type))
359                .map(|mut u| {
360                    let field_name = if let Some(field_name) = elem.attr("name") {
361                        field_name.into()
362                    } else {
363                        format!("u{union_count}")
364                    };
365
366                    u = Union {
367                        name: format!(
368                            "{}{}_{}",
369                            parent_name_prefix.map_or_else(String::new, |s| { format!("{s}_") }),
370                            record_name,
371                            field_name
372                        ),
373                        c_type: Some(format!(
374                            "{}{}_{}",
375                            parent_ctype_prefix.map_or_else(String::new, |s| { format!("{s}_") }),
376                            c_type,
377                            field_name
378                        )),
379                        ..u
380                    };
381
382                    let u_doc = u.doc.clone();
383                    let ctype = u.c_type.clone();
384
385                    fields.push(Field {
386                        name: field_name,
387                        typ: Type::union(self, u, ns_id),
388                        doc: u_doc,
389                        c_type: ctype,
390                        ..Field::default()
391                    });
392                    union_count += 1;
393                }),
394            "field" => {
395                self.read_field(parser, ns_id, elem).map(|mut f| {
396                    // Workaround for bitfields
397                    if c_type == "GDate" {
398                        if f.name == "julian_days" {
399                            fields.push(f);
400                        } else if f.name == "julian" {
401                            f.name = "flags_dmy".into();
402                            f.typ = TypeId::tid_uint32();
403                            f.c_type = Some("guint".into());
404                            f.bits = None;
405                            fields.push(f);
406                        } else {
407                            // Skip
408                        }
409                        return;
410                    }
411                    // Workaround for wrong GValue c:type
412                    if c_type == "GValue" && f.name == "data" {
413                        f.c_type = Some("GValue_data".into());
414                    }
415                    fields.push(f);
416                })
417            }
418            "doc" => parser.text().map(|t| doc = Some(t)),
419            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
420            "doc-version" => parser.ignore_element(),
421            "source-position" => parser.ignore_element(),
422            "attribute" => parser.ignore_element(),
423            "method-inline" => parser.ignore_element(),
424            _ => Err(parser.unexpected_element(elem)),
425        })?;
426
427        let typ = Type::Record(Record {
428            name: record_name.into(),
429            c_type: c_type.into(),
430            glib_get_type: get_type,
431            functions: if is_class_record && gtype_struct_for.is_some() {
432                fns.into_iter()
433                    .map(|mut f| {
434                        f.kind = FunctionKind::ClassMethod;
435                        f
436                    })
437                    .collect::<Vec<_>>()
438            } else {
439                fns
440            },
441            gtype_struct_for: gtype_struct_for.map(|s| s.into()),
442            fields,
443            version,
444            deprecated_version,
445            doc,
446            doc_deprecated,
447            disguised,
448            pointer,
449            symbol_prefix,
450        });
451
452        Ok(Some(typ))
453    }
454
455    fn read_named_union(
456        &mut self,
457        parser: &mut XmlParser<'_>,
458        ns_id: u16,
459        elem: &Element,
460    ) -> Result<(), String> {
461        // Require a name here
462        elem.attr_required("name")?;
463
464        self.read_union(parser, ns_id, elem, None, None)
465            .and_then(|mut u| {
466                assert_ne!(u.name, "");
467                // Workaround for missing c:type
468                if u.name == "_Value__data__union" {
469                    u.c_type = Some("GValue_data".into());
470                } else if u.c_type.is_none() {
471                    return Err(parser.fail("Missing union c:type"));
472                }
473                let union_name = u.name.clone();
474                self.add_type(ns_id, &union_name, Type::Union(u));
475                Ok(())
476            })
477    }
478
479    fn read_union(
480        &mut self,
481        parser: &mut XmlParser<'_>,
482        ns_id: u16,
483        elem: &Element,
484        parent_name_prefix: Option<&str>,
485        parent_ctype_prefix: Option<&str>,
486    ) -> Result<Union, String> {
487        let union_name = elem.attr("name").unwrap_or("");
488        let c_type = self.read_object_c_type(parser, elem).unwrap_or("");
489        let get_type = elem.attr("get-type").map(|s| s.into());
490        let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned);
491
492        let mut fields = Vec::new();
493        let mut fns = Vec::new();
494        let mut doc = None;
495        let mut struct_count = 1;
496
497        parser.elements(|parser, elem| match elem.name() {
498            "source-position" => parser.ignore_element(),
499            "field" => self.read_field(parser, ns_id, elem).map(|f| {
500                fields.push(f);
501            }),
502            "constructor" | "function" | "method" => {
503                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
504            }
505            "record" => {
506                let mut r = match self.read_record(
507                    parser,
508                    ns_id,
509                    elem,
510                    parent_name_prefix,
511                    parent_ctype_prefix,
512                )? {
513                    Some(Type::Record(r)) => r,
514                    _ => return Ok(()),
515                };
516
517                let field_name = if let Some(field_name) = elem.attr("name") {
518                    field_name.into()
519                } else {
520                    format!("s{struct_count}")
521                };
522
523                r = Record {
524                    name: format!(
525                        "{}{}_{}",
526                        parent_name_prefix.map_or_else(String::new, |s| { format!("{s}_") }),
527                        union_name,
528                        field_name
529                    ),
530                    c_type: format!(
531                        "{}{}_{}",
532                        parent_ctype_prefix.map_or_else(String::new, |s| { format!("{s}_") }),
533                        c_type,
534                        field_name
535                    ),
536                    ..r
537                };
538
539                let r_doc = r.doc.clone();
540                let ctype = r.c_type.clone();
541
542                fields.push(Field {
543                    name: field_name,
544                    typ: Type::record(self, r, ns_id),
545                    doc: r_doc,
546                    c_type: Some(ctype),
547                    ..Field::default()
548                });
549
550                struct_count += 1;
551
552                Ok(())
553            }
554            "doc" => parser.text().map(|t| doc = Some(t)),
555            "attribute" => parser.ignore_element(),
556            _ => Err(parser.unexpected_element(elem)),
557        })?;
558
559        Ok(Union {
560            name: union_name.into(),
561            c_type: Some(c_type.into()),
562            glib_get_type: get_type,
563            fields,
564            functions: fns,
565            doc,
566            symbol_prefix,
567        })
568    }
569
570    fn read_virtual_method(
571        &mut self,
572        parser: &mut XmlParser,
573        ns_id: u16,
574        elem: &Element,
575    ) -> Result<Function, String> {
576        let method_name = elem.attr_required("name")?;
577        let version = self.read_version(parser, ns_id, elem)?;
578        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
579        let c_identifier = elem.attr("identifier").or_else(|| elem.attr("name"));
580        let mut params = Vec::new();
581        let mut ret = None;
582        let mut doc = None;
583        let mut doc_deprecated = None;
584
585        parser.elements(|parser, elem| match elem.name() {
586            "parameters" => self
587                .read_parameters(parser, ns_id, true, true)
588                .map(|mut ps| params.append(&mut ps)),
589            "return-value" => {
590                if ret.is_some() {
591                    return Err(parser.fail("Too many <return-value> elements"));
592                }
593                self.read_parameter(parser, ns_id, elem, true, false)
594                    .map(|p| ret = Some(p))
595            }
596            "source-position" => parser.ignore_element(),
597            "doc" => parser.text().map(|t| doc = Some(t)),
598            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
599            "doc-version" => parser.ignore_element(),
600            "attribute" => parser.ignore_element(),
601            _ => Err(parser.unexpected_element(elem)),
602        })?;
603
604        let throws = elem.attr_bool("throws", false);
605        if throws {
606            params.push(Parameter {
607                name: "error".into(),
608                typ: self.find_or_stub_type(ns_id, "GLib.Error"),
609                c_type: "GError**".into(),
610                instance_parameter: false,
611                direction: ParameterDirection::Out,
612                transfer: Transfer::Full,
613                caller_allocates: false,
614                nullable: Nullable(true),
615                array_length: None,
616                is_error: true,
617                doc: None,
618                scope: ParameterScope::None,
619                closure: None,
620                destroy: None,
621            });
622        }
623
624        if let Some(ret) = ret {
625            Ok(Function {
626                name: method_name.into(),
627                c_identifier: c_identifier.map(|s| s.into()),
628                kind: FunctionKind::VirtualMethod,
629                parameters: params,
630                ret,
631                throws,
632                version,
633                deprecated_version,
634                doc,
635                doc_deprecated,
636                get_property: None,
637                set_property: None,
638                finish_func: None,
639                async_func: None,
640                sync_func: None,
641            })
642        } else {
643            Err(parser.fail("Missing <return-value> element"))
644        }
645    }
646
647    fn read_field(
648        &mut self,
649        parser: &mut XmlParser<'_>,
650        ns_id: u16,
651        elem: &Element,
652    ) -> Result<Field, String> {
653        let field_name = elem.attr_required("name")?;
654        let private = elem.attr_bool("private", false);
655        let bits = elem.attr("bits").and_then(|s| s.parse().ok());
656
657        let mut typ = None;
658        let mut doc = None;
659
660        parser.elements(|parser, elem| match elem.name() {
661            "type" | "array" => {
662                if typ.is_some() {
663                    return Err(parser.fail("Too many <type> elements"));
664                }
665                self.read_type(parser, ns_id, elem).map(|t| {
666                    typ = Some(t);
667                })
668            }
669            "callback" => {
670                if typ.is_some() {
671                    return Err(parser.fail("Too many <type> elements"));
672                }
673                self.read_function(parser, ns_id, elem.name(), elem, true)
674                    .map(|f| {
675                        typ = Some((Type::function(self, f), None, None));
676                    })
677            }
678            "doc" => parser.text().map(|t| doc = Some(t)),
679            "source-position" => parser.ignore_element(),
680            "attribute" => parser.ignore_element(),
681            _ => Err(parser.unexpected_element(elem)),
682        })?;
683
684        if let Some((tid, c_type, array_length)) = typ {
685            Ok(Field {
686                name: field_name.into(),
687                typ: tid,
688                c_type,
689                private,
690                bits,
691                array_length,
692                doc,
693            })
694        } else {
695            Err(parser.fail("Missing <type> element"))
696        }
697    }
698
699    fn read_named_callback(
700        &mut self,
701        parser: &mut XmlParser<'_>,
702        ns_id: u16,
703        elem: &Element,
704    ) -> Result<(), String> {
705        self.read_function_if_not_moved(parser, ns_id, elem.name(), elem, true)?
706            .map(|func| {
707                let name = func.name.clone();
708                self.add_type(ns_id, &name, Type::Function(func))
709            });
710
711        Ok(())
712    }
713
714    fn read_interface(
715        &mut self,
716        parser: &mut XmlParser<'_>,
717        ns_id: u16,
718        elem: &Element,
719    ) -> Result<(), String> {
720        let interface_name = elem.attr_required("name")?;
721        let c_type = self.read_object_c_type(parser, elem)?;
722        let symbol_prefix = elem.attr_required("symbol-prefix").map(ToOwned::to_owned)?;
723        let type_struct = elem.attr("type-struct").map(ToOwned::to_owned);
724        let get_type = elem.attr_required("get-type")?;
725        let version = self.read_version(parser, ns_id, elem)?;
726        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
727
728        let mut fns = Vec::new();
729        let mut vfns = Vec::new();
730        let mut signals = Vec::new();
731        let mut properties = Vec::new();
732        let mut prereqs = Vec::new();
733        let mut doc = None;
734        let mut doc_deprecated = None;
735
736        parser.elements(|parser, elem| match elem.name() {
737            "constructor" | "function" | "method" => {
738                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
739            }
740            "prerequisite" => self.read_type(parser, ns_id, elem).map(|r| {
741                prereqs.push(r.0);
742            }),
743            "signal" => self
744                .read_signal(parser, ns_id, elem)
745                .map(|s| signals.push(s)),
746            "property" => self
747                .read_property(parser, ns_id, elem, &symbol_prefix)
748                .map(|p| {
749                    if let Some(p) = p {
750                        properties.push(p);
751                    }
752                }),
753            "doc" => parser.text().map(|t| doc = Some(t)),
754            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
755            "doc-version" => parser.ignore_element(),
756            "virtual-method" => self
757                .read_virtual_method(parser, ns_id, elem)
758                .map(|v| vfns.push(v)),
759            "source-position" => parser.ignore_element(),
760            "attribute" => parser.ignore_element(),
761            _ => Err(parser.unexpected_element(elem)),
762        })?;
763
764        let typ = Type::Interface(Interface {
765            name: interface_name.into(),
766            c_type: c_type.into(),
767            type_struct,
768            c_class_type: None, // this will be resolved during postprocessing
769            glib_get_type: get_type.into(),
770            functions: fns,
771            virtual_methods: vfns,
772            signals,
773            properties,
774            prerequisites: prereqs,
775            doc,
776            doc_deprecated,
777            version,
778            deprecated_version,
779            symbol_prefix,
780        });
781        self.add_type(ns_id, interface_name, typ);
782        Ok(())
783    }
784
785    fn read_bitfield(
786        &mut self,
787        parser: &mut XmlParser<'_>,
788        ns_id: u16,
789        elem: &Element,
790    ) -> Result<(), String> {
791        let bitfield_name = elem.attr_required("name")?;
792        let c_type = self.read_object_c_type(parser, elem)?;
793        let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned);
794        let get_type = elem.attr("get-type").map(|s| s.into());
795        let version = self.read_version(parser, ns_id, elem)?;
796        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
797
798        let mut members = Vec::new();
799        let mut fns = Vec::new();
800        let mut doc = None;
801        let mut doc_deprecated = None;
802
803        parser.elements(|parser, elem| match elem.name() {
804            "member" => self
805                .read_member(parser, ns_id, elem)
806                .map(|m| members.push(m)),
807            "constructor" | "function" | "method" => {
808                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
809            }
810            "doc" => parser.text().map(|t| doc = Some(t)),
811            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
812            "doc-version" => parser.ignore_element(),
813            "source-position" => parser.ignore_element(),
814            "attribute" => parser.ignore_element(),
815            _ => Err(parser.unexpected_element(elem)),
816        })?;
817
818        let typ = Type::Bitfield(Bitfield {
819            name: bitfield_name.into(),
820            c_type: c_type.into(),
821            members,
822            functions: fns,
823            version,
824            deprecated_version,
825            doc,
826            doc_deprecated,
827            glib_get_type: get_type,
828            symbol_prefix,
829        });
830        self.add_type(ns_id, bitfield_name, typ);
831        Ok(())
832    }
833
834    fn read_enumeration(
835        &mut self,
836        parser: &mut XmlParser<'_>,
837        ns_id: u16,
838        elem: &Element,
839    ) -> Result<(), String> {
840        let enum_name = elem.attr_required("name")?;
841        let c_type = self.read_object_c_type(parser, elem)?;
842        let symbol_prefix = elem.attr("symbol-prefix").map(ToOwned::to_owned);
843        let get_type = elem.attr("get-type").map(|s| s.into());
844        let version = self.read_version(parser, ns_id, elem)?;
845        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
846        let error_domain = elem
847            .attr("error-domain")
848            .map(|s| ErrorDomain::Quark(String::from(s)));
849
850        let mut members = Vec::new();
851        let mut fns = Vec::new();
852        let mut doc = None;
853        let mut doc_deprecated = None;
854
855        parser.elements(|parser, elem| match elem.name() {
856            "member" => self
857                .read_member(parser, ns_id, elem)
858                .map(|m| members.push(m)),
859            "constructor" | "function" | "method" => {
860                self.read_function_to_vec(parser, ns_id, elem, &mut fns)
861            }
862            "doc" => parser.text().map(|t| doc = Some(t)),
863            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
864            "doc-version" => parser.ignore_element(),
865            "source-position" => parser.ignore_element(),
866            "attribute" => parser.ignore_element(),
867            _ => Err(parser.unexpected_element(elem)),
868        })?;
869
870        let typ = Type::Enumeration(Enumeration {
871            name: enum_name.into(),
872            c_type: c_type.into(),
873            members,
874            functions: fns,
875            version,
876            deprecated_version,
877            doc,
878            doc_deprecated,
879            error_domain,
880            glib_get_type: get_type,
881            symbol_prefix,
882        });
883        self.add_type(ns_id, enum_name, typ);
884        Ok(())
885    }
886
887    fn read_global_function(
888        &mut self,
889        parser: &mut XmlParser<'_>,
890        ns_id: u16,
891        elem: &Element,
892    ) -> Result<(), String> {
893        self.read_function_if_not_moved(parser, ns_id, "global", elem, false)
894            .map(|func| {
895                if let Some(func) = func {
896                    self.add_function(ns_id, func);
897                }
898            })
899    }
900
901    fn read_constant(
902        &mut self,
903        parser: &mut XmlParser<'_>,
904        ns_id: u16,
905        elem: &Element,
906    ) -> Result<(), String> {
907        let const_name = elem.attr_required("name")?;
908        let c_identifier = elem.attr_required("type")?;
909        let value = elem.attr_required("value")?;
910        let version = self.read_version(parser, ns_id, elem)?;
911        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
912
913        let mut inner = None;
914        let mut doc = None;
915        let mut doc_deprecated = None;
916
917        parser.elements(|parser, elem| match elem.name() {
918            "type" | "array" => {
919                if inner.is_some() {
920                    return Err(parser.fail_with_position(
921                        "Too many <type> inner elements in <constant> element",
922                        elem.position(),
923                    ));
924                }
925                let (typ, c_type, array_length) = self.read_type(parser, ns_id, elem)?;
926                if let Some(c_type) = c_type {
927                    inner = Some((typ, c_type, array_length));
928                } else {
929                    return Err(parser.fail_with_position(
930                        "Missing <constant> element's c:type",
931                        elem.position(),
932                    ));
933                }
934                Ok(())
935            }
936            "doc" => parser.text().map(|t| doc = Some(t)),
937            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
938            "doc-version" => parser.ignore_element(),
939            "source-position" => parser.ignore_element(),
940            "attribute" => parser.ignore_element(),
941            _ => Err(parser.unexpected_element(elem)),
942        })?;
943
944        if let Some((typ, c_type, _array_length)) = inner {
945            self.add_constant(
946                ns_id,
947                Constant {
948                    name: const_name.into(),
949                    c_identifier: c_identifier.into(),
950                    typ,
951                    c_type,
952                    value: value.into(),
953                    version,
954                    deprecated_version,
955                    doc,
956                    doc_deprecated,
957                },
958            );
959            Ok(())
960        } else {
961            Err(parser.fail_with_position(
962                "Missing <type> element inside <constant> element",
963                elem.position(),
964            ))
965        }
966    }
967
968    fn read_alias(
969        &mut self,
970        parser: &mut XmlParser<'_>,
971        ns_id: u16,
972        elem: &Element,
973    ) -> Result<(), String> {
974        let alias_name = elem.attr_required("name")?;
975        let c_identifier = elem.attr_required("type")?;
976
977        let mut inner = None;
978        let mut doc = None;
979        let mut doc_deprecated = None;
980
981        parser.elements(|parser, elem| match elem.name() {
982            "source-position" => parser.ignore_element(),
983            "type" | "array" => {
984                if inner.is_some() {
985                    return Err(parser.fail_with_position(
986                        "Too many <type> inner elements in <alias> element",
987                        elem.position(),
988                    ));
989                }
990                let (typ, c_type, array_length) = self.read_type(parser, ns_id, elem)?;
991                if let Some(c_type) = c_type {
992                    inner = Some((typ, c_type, array_length));
993                } else {
994                    return Err(parser.fail("Missing <alias> target's c:type"));
995                }
996                Ok(())
997            }
998            "doc" => parser.text().map(|t| doc = Some(t)),
999            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
1000            "doc-version" => parser.ignore_element(),
1001            "attribute" => parser.ignore_element(),
1002            _ => Err(parser.unexpected_element(elem)),
1003        })?;
1004
1005        if let Some((typ, c_type, _array_length)) = inner {
1006            let typ = Type::Alias(Alias {
1007                name: alias_name.into(),
1008                c_identifier: c_identifier.into(),
1009                typ,
1010                target_c_type: c_type,
1011                doc,
1012                doc_deprecated,
1013            });
1014            self.add_type(ns_id, alias_name, typ);
1015            Ok(())
1016        } else {
1017            Err(parser.fail_with_position(
1018                "Missing <type> element inside <alias> element",
1019                elem.position(),
1020            ))
1021        }
1022    }
1023
1024    fn read_member(
1025        &mut self,
1026        parser: &mut XmlParser<'_>,
1027        ns_id: u16,
1028        elem: &Element,
1029    ) -> Result<Member, String> {
1030        let member_name = elem.attr_required("name")?;
1031        let value = elem.attr_required("value")?;
1032        let c_identifier = elem.attr("identifier").map(|x| x.into());
1033        let version = self.read_version(parser, ns_id, elem)?;
1034        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
1035
1036        let mut doc = None;
1037        let mut doc_deprecated = None;
1038
1039        parser.elements(|parser, elem| match elem.name() {
1040            "doc" => parser.text().map(|t| doc = Some(t)),
1041            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
1042            "doc-version" => parser.ignore_element(),
1043            "attribute" => parser.ignore_element(),
1044            _ => Err(parser.unexpected_element(elem)),
1045        })?;
1046
1047        Ok(Member {
1048            name: member_name.into(),
1049            value: value.into(),
1050            doc,
1051            doc_deprecated,
1052            c_identifier: c_identifier.unwrap_or_else(|| member_name.into()),
1053            status: crate::config::gobjects::GStatus::Generate,
1054            version,
1055            deprecated_version,
1056        })
1057    }
1058
1059    fn read_function(
1060        &mut self,
1061        parser: &mut XmlParser<'_>,
1062        ns_id: u16,
1063        kind_str: &str,
1064        elem: &Element,
1065        is_callback: bool,
1066    ) -> Result<Function, String> {
1067        let fn_name = elem.attr_required("name")?;
1068        let c_identifier = elem.attr("identifier").or_else(|| elem.attr("type"));
1069        let kind = FunctionKind::from_str(kind_str).map_err(|why| parser.fail(&why))?;
1070        let is_method = kind == FunctionKind::Method;
1071        let version = self.read_version(parser, ns_id, elem)?;
1072        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
1073        let mut gtk_get_property = None;
1074        let mut gtk_set_property = None;
1075        let finish_func = c_identifier.and_then(|c_identifier| {
1076            elem.attr("finish-func").map(|finish_func_name| {
1077                format!(
1078                    "{}{finish_func_name}",
1079                    c_identifier.strip_suffix(&fn_name).unwrap()
1080                )
1081            })
1082        });
1083        let async_func = elem.attr("async-func").map(ToString::to_string);
1084        let sync_func = elem.attr("sync-func").map(ToString::to_string);
1085
1086        let mut params = Vec::new();
1087        let mut ret = None;
1088        let mut doc = None;
1089        let mut doc_deprecated = None;
1090
1091        parser.elements(|parser, elem| match elem.name() {
1092            "parameters" => self
1093                .read_parameters(parser, ns_id, false, is_method)
1094                .map(|mut ps| params.append(&mut ps)),
1095            "return-value" => {
1096                if ret.is_some() {
1097                    return Err(parser.fail_with_position(
1098                        "Too many <return-value> elements inside <function> element",
1099                        elem.position(),
1100                    ));
1101                }
1102                ret = Some(self.read_parameter(parser, ns_id, elem, false, is_method)?);
1103                Ok(())
1104            }
1105            "doc" => parser.text().map(|t| doc = Some(t)),
1106            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
1107            "doc-version" => parser.ignore_element(),
1108            "source-position" => parser.ignore_element(),
1109            "attribute" => {
1110                if let (Some(name), Some(value)) = (elem.attr("name"), elem.attr("value")) {
1111                    match name {
1112                        "org.gtk.Method.get_property" => {
1113                            gtk_get_property = Some(value.to_string());
1114                            Ok(())
1115                        }
1116                        "org.gtk.Method.set_property" => {
1117                            gtk_set_property = Some(value.to_string());
1118                            Ok(())
1119                        }
1120                        _ => parser.ignore_element(),
1121                    }
1122                } else {
1123                    parser.ignore_element()
1124                }
1125            }
1126            _ => Err(parser.unexpected_element(elem)),
1127        })?;
1128
1129        let get_property = gtk_get_property.or(elem.attr("get-property").map(ToString::to_string));
1130        let set_property = gtk_set_property.or(elem.attr("set-property").map(ToString::to_string));
1131        // The last argument of a callback is ALWAYS user data, so it has to be marked as such
1132        // in case it's missing.
1133        if is_callback && params.last().map(|x| x.closure.is_none()).unwrap_or(false) {
1134            params.last_mut().unwrap().closure = Some(2000);
1135        }
1136
1137        let throws = elem.attr_bool("throws", false);
1138        if throws {
1139            params.push(Parameter {
1140                name: "error".into(),
1141                typ: self.find_or_stub_type(ns_id, "GLib.Error"),
1142                c_type: "GError**".into(),
1143                instance_parameter: false,
1144                direction: ParameterDirection::Out,
1145                transfer: Transfer::Full,
1146                caller_allocates: false,
1147                nullable: Nullable(true),
1148                array_length: None,
1149                is_error: true,
1150                doc: None,
1151                scope: ParameterScope::None,
1152                closure: None,
1153                destroy: None,
1154            });
1155        }
1156        if let Some(ret) = ret {
1157            Ok(Function {
1158                name: fn_name.into(),
1159                c_identifier: c_identifier.map(|s| s.into()),
1160                kind,
1161                parameters: params,
1162                ret,
1163                throws,
1164                version,
1165                deprecated_version,
1166                doc,
1167                doc_deprecated,
1168                get_property,
1169                set_property,
1170                finish_func,
1171                async_func,
1172                sync_func,
1173            })
1174        } else {
1175            Err(parser.fail_with_position(
1176                "Missing <return-value> element in <function> element",
1177                elem.position(),
1178            ))
1179        }
1180    }
1181
1182    fn read_function_to_vec(
1183        &mut self,
1184        parser: &mut XmlParser<'_>,
1185        ns_id: u16,
1186        elem: &Element,
1187        fns: &mut Vec<Function>,
1188    ) -> Result<(), String> {
1189        if let Some(f) = self.read_function_if_not_moved(parser, ns_id, elem.name(), elem, false)? {
1190            fns.push(f);
1191        }
1192        Ok(())
1193    }
1194
1195    fn read_function_if_not_moved(
1196        &mut self,
1197        parser: &mut XmlParser<'_>,
1198        ns_id: u16,
1199        kind_str: &str,
1200        elem: &Element,
1201        is_callback: bool,
1202    ) -> Result<Option<Function>, String> {
1203        if elem.attr("moved-to").is_some() {
1204            return parser.ignore_element().map(|_| None);
1205        }
1206        self.read_function(parser, ns_id, kind_str, elem, is_callback)
1207            .and_then(|f| {
1208                if f.c_identifier.is_none() {
1209                    return Err(parser.fail_with_position(
1210                        &format!(
1211                            "Missing c:identifier attribute in <{}> element",
1212                            elem.name()
1213                        ),
1214                        elem.position(),
1215                    ));
1216                }
1217                Ok(Some(f))
1218            })
1219    }
1220
1221    fn read_signal(
1222        &mut self,
1223        parser: &mut XmlParser<'_>,
1224        ns_id: u16,
1225        elem: &Element,
1226    ) -> Result<Signal, String> {
1227        let signal_name = elem.attr_required("name")?;
1228        let is_action = elem.attr_bool("action", false);
1229        let is_detailed = elem.attr_bool("detailed", false);
1230        let version = self.read_version(parser, ns_id, elem)?;
1231        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
1232
1233        let mut params = Vec::new();
1234        let mut ret = None;
1235        let mut doc = None;
1236        let mut doc_deprecated = None;
1237
1238        parser.elements(|parser, elem| match elem.name() {
1239            "parameters" => self
1240                .read_parameters(parser, ns_id, true, false)
1241                .map(|mut ps| params.append(&mut ps)),
1242            "return-value" => {
1243                if ret.is_some() {
1244                    return Err(parser.fail_with_position(
1245                        "Too many <return-value> elements in <signal> element",
1246                        elem.position(),
1247                    ));
1248                }
1249                self.read_parameter(parser, ns_id, elem, true, false)
1250                    .map(|p| ret = Some(p))
1251            }
1252            "doc" => parser.text().map(|t| doc = Some(t)),
1253            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
1254            "doc-version" => parser.ignore_element(),
1255            "source-position" => parser.ignore_element(),
1256            "attribute" => parser.ignore_element(),
1257            _ => Err(parser.unexpected_element(elem)),
1258        })?;
1259        if let Some(ret) = ret {
1260            Ok(Signal {
1261                name: signal_name.into(),
1262                parameters: params,
1263                ret,
1264                is_action,
1265                is_detailed,
1266                version,
1267                deprecated_version,
1268                doc,
1269                doc_deprecated,
1270            })
1271        } else {
1272            Err(parser.fail_with_position(
1273                "Missing <return-value> element in <signal> element",
1274                elem.position(),
1275            ))
1276        }
1277    }
1278
1279    fn read_parameters(
1280        &mut self,
1281        parser: &mut XmlParser<'_>,
1282        ns_id: u16,
1283        allow_no_ctype: bool,
1284        for_method: bool,
1285    ) -> Result<Vec<Parameter>, String> {
1286        parser.elements(|parser, elem| match elem.name() {
1287            "parameter" | "instance-parameter" => {
1288                self.read_parameter(parser, ns_id, elem, allow_no_ctype, for_method)
1289            }
1290            _ => Err(parser.unexpected_element(elem)),
1291        })
1292    }
1293
1294    fn read_parameter(
1295        &mut self,
1296        parser: &mut XmlParser<'_>,
1297        ns_id: u16,
1298        elem: &Element,
1299        allow_no_ctype: bool,
1300        for_method: bool,
1301    ) -> Result<Parameter, String> {
1302        let param_name = elem.attr("name").unwrap_or("");
1303        let instance_parameter = elem.name() == "instance-parameter";
1304        let transfer = elem
1305            .attr_from_str("transfer-ownership")?
1306            .unwrap_or(Transfer::None);
1307        let nullable = elem.attr_bool("nullable", false);
1308        let scope = elem.attr_from_str("scope")?.unwrap_or(ParameterScope::None);
1309        let closure = elem.attr_from_str("closure")?;
1310        let destroy = elem.attr_from_str("destroy")?;
1311        let caller_allocates = elem.attr_bool("caller-allocates", false);
1312        let direction = if elem.name() == "return-value" {
1313            Ok(ParameterDirection::Return)
1314        } else {
1315            ParameterDirection::from_str(elem.attr("direction").unwrap_or("in"))
1316                .map_err(|why| parser.fail_with_position(&why, elem.position()))
1317        }?;
1318
1319        let mut typ = None;
1320        let mut varargs = false;
1321        let mut doc = None;
1322
1323        parser.elements(|parser, elem| match elem.name() {
1324            "type" | "array" => {
1325                if typ.is_some() {
1326                    return Err(parser.fail_with_position(
1327                        &format!("Too many <type> elements in <{}> element", elem.name()),
1328                        elem.position(),
1329                    ));
1330                }
1331                typ = Some(self.read_type(parser, ns_id, elem)?);
1332                if let Some((tid, None, _)) = typ {
1333                    if allow_no_ctype {
1334                        typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None));
1335                    } else {
1336                        return Err(parser.fail_with_position(
1337                            &format!("Missing c:type attribute in <{}> element", elem.name()),
1338                            elem.position(),
1339                        ));
1340                    }
1341                }
1342                Ok(())
1343            }
1344            "varargs" => {
1345                varargs = true;
1346                parser.ignore_element()
1347            }
1348            "doc" => parser.text().map(|t| doc = Some(t)),
1349            "attribute" => parser.ignore_element(),
1350            _ => Err(parser.unexpected_element(elem)),
1351        })?;
1352
1353        if let Some((tid, c_type, mut array_length)) = typ {
1354            if for_method {
1355                array_length = array_length.map(|l| l + 1);
1356            }
1357            Ok(Parameter {
1358                name: param_name.into(),
1359                typ: tid,
1360                c_type: c_type.unwrap(),
1361                instance_parameter,
1362                direction,
1363                transfer,
1364                caller_allocates,
1365                nullable: Nullable(nullable),
1366                array_length,
1367                is_error: false,
1368                doc,
1369                scope,
1370                closure,
1371                destroy,
1372            })
1373        } else if varargs {
1374            Ok(Parameter {
1375                name: String::new(),
1376                typ: self.find_type(INTERNAL_NAMESPACE, "varargs").unwrap(),
1377                c_type: String::new(),
1378                instance_parameter,
1379                direction: Default::default(),
1380                transfer: Transfer::None,
1381                caller_allocates: false,
1382                nullable: Nullable(false),
1383                array_length: None,
1384                is_error: false,
1385                doc,
1386                scope,
1387                closure,
1388                destroy,
1389            })
1390        } else {
1391            Err(parser.fail_with_position(
1392                &format!("Missing <type> element in <{}> element", elem.name()),
1393                elem.position(),
1394            ))
1395        }
1396    }
1397
1398    fn read_property(
1399        &mut self,
1400        parser: &mut XmlParser<'_>,
1401        ns_id: u16,
1402        elem: &Element,
1403        symbol_prefix: &str,
1404    ) -> Result<Option<Property>, String> {
1405        let prop_name = elem.attr_required("name")?;
1406        let readable = elem.attr_bool("readable", true);
1407        let writable = elem.attr_bool("writable", false);
1408        let construct = elem.attr_bool("construct", false);
1409        let construct_only = elem.attr_bool("construct-only", false);
1410        let transfer = Transfer::from_str(elem.attr("transfer-ownership").unwrap_or("none"))
1411            .map_err(|why| parser.fail_with_position(&why, elem.position()))?;
1412
1413        let version = self.read_version(parser, ns_id, elem)?;
1414        let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?;
1415        let mut has_empty_type_tag = false;
1416        let mut typ = None;
1417        let mut doc = None;
1418        let mut doc_deprecated = None;
1419        let mut gtk_getter = None;
1420        let mut gtk_setter = None;
1421
1422        parser.elements(|parser, elem| match elem.name() {
1423            "type" | "array" => {
1424                if typ.is_some() {
1425                    return Err(parser.fail_with_position(
1426                        "Too many <type> elements in <property> element",
1427                        elem.position(),
1428                    ));
1429                }
1430                if !elem.has_attrs() && elem.name() == "type" {
1431                    // defend from <type/>
1432                    has_empty_type_tag = true;
1433                    return parser.ignore_element();
1434                }
1435                typ = Some(self.read_type(parser, ns_id, elem)?);
1436                if let Some((tid, None, _)) = typ {
1437                    typ = Some((tid, Some(EMPTY_CTYPE.to_owned()), None));
1438                }
1439                Ok(())
1440            }
1441            "doc" => parser.text().map(|t| doc = Some(t)),
1442            "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)),
1443            "doc-version" => parser.ignore_element(),
1444            "source-position" => parser.ignore_element(),
1445            "attribute" => {
1446                if let (Some(name), Some(value)) = (elem.attr("name"), elem.attr("value")) {
1447                    match name {
1448                        "org.gtk.Property.get" => {
1449                            gtk_getter = value
1450                                .split(symbol_prefix)
1451                                .last()
1452                                .and_then(|p| p.strip_prefix('_'))
1453                                .map(|p| p.to_string());
1454                            Ok(())
1455                        }
1456                        "org.gtk.Property.set" => {
1457                            gtk_setter = value
1458                                .split(symbol_prefix)
1459                                .last()
1460                                .and_then(|p| p.strip_prefix('_'))
1461                                .map(|p| p.to_string());
1462                            Ok(())
1463                        }
1464                        _ => parser.ignore_element(),
1465                    }
1466                } else {
1467                    parser.ignore_element()
1468                }
1469            }
1470            _ => Err(parser.unexpected_element(elem)),
1471        })?;
1472
1473        let getter = gtk_getter.or(elem.attr("getter").map(ToString::to_string));
1474        let setter = gtk_setter.or(elem.attr("setter").map(ToString::to_string));
1475        if has_empty_type_tag {
1476            return Ok(None);
1477        }
1478
1479        if let Some((tid, c_type, _array_length)) = typ {
1480            Ok(Some(Property {
1481                name: prop_name.into(),
1482                readable,
1483                writable,
1484                construct,
1485                construct_only,
1486                transfer,
1487                typ: tid,
1488                c_type,
1489                version,
1490                deprecated_version,
1491                doc,
1492                doc_deprecated,
1493                getter,
1494                setter,
1495            }))
1496        } else {
1497            Err(parser.fail_with_position(
1498                "Missing <type> element in <property> element",
1499                elem.position(),
1500            ))
1501        }
1502    }
1503
1504    fn read_type(
1505        &mut self,
1506        parser: &mut XmlParser<'_>,
1507        ns_id: u16,
1508        elem: &Element,
1509    ) -> Result<(TypeId, Option<String>, Option<u32>), String> {
1510        let type_name = elem
1511            .attr("name")
1512            .or_else(|| {
1513                if elem.name() == "array" {
1514                    Some("array")
1515                } else {
1516                    None
1517                }
1518            })
1519            .ok_or_else(|| {
1520                parser.fail_with_position(
1521                    "<type> element is missing a name attribute",
1522                    elem.position(),
1523                )
1524            })?;
1525        let c_type = elem.attr("type").map(|s| s.into());
1526        let array_length = elem.attr("length").and_then(|s| s.parse().ok());
1527
1528        let inner = parser.elements(|parser, elem| match elem.name() {
1529            "type" | "array" => self.read_type(parser, ns_id, elem),
1530            _ => Err(parser.unexpected_element(elem)),
1531        })?;
1532
1533        if inner.is_empty() || type_name == "GLib.ByteArray" {
1534            if type_name == "array" {
1535                Err(parser.fail_with_position(
1536                    "<type> element is missing an inner element type",
1537                    elem.position(),
1538                ))
1539            } else if type_name == "gboolean"
1540                && c_type
1541                    .as_ref()
1542                    .is_some_and(|c_type| c_type == "_Bool" || c_type == "bool")
1543            {
1544                Ok((self.find_or_stub_type(ns_id, "bool"), c_type, array_length))
1545            } else {
1546                Ok((
1547                    self.find_or_stub_type(ns_id, type_name),
1548                    c_type,
1549                    array_length,
1550                ))
1551            }
1552        } else {
1553            let tid = if type_name == "array" {
1554                let inner_type = &inner[0];
1555                Type::c_array(
1556                    self,
1557                    inner_type.0,
1558                    elem.attr("fixed-size").and_then(|n| n.parse().ok()),
1559                    inner_type.1.clone(),
1560                )
1561            } else {
1562                let inner = inner.iter().map(|r| r.0).collect();
1563                Type::container(self, type_name, inner).ok_or_else(|| {
1564                    parser.fail_with_position("Unknown container type", elem.position())
1565                })?
1566            };
1567            Ok((tid, c_type, array_length))
1568        }
1569    }
1570
1571    fn read_version(
1572        &mut self,
1573        parser: &XmlParser<'_>,
1574        ns_id: u16,
1575        elem: &Element,
1576    ) -> Result<Option<Version>, String> {
1577        self.read_version_attribute(parser, ns_id, elem, "version")
1578    }
1579
1580    fn read_deprecated_version(
1581        &mut self,
1582        parser: &XmlParser<'_>,
1583        ns_id: u16,
1584        elem: &Element,
1585    ) -> Result<Option<Version>, String> {
1586        self.read_version_attribute(parser, ns_id, elem, "deprecated-version")
1587    }
1588
1589    fn read_version_attribute(
1590        &mut self,
1591        parser: &XmlParser<'_>,
1592        ns_id: u16,
1593        elem: &Element,
1594        attr: &str,
1595    ) -> Result<Option<Version>, String> {
1596        if let Some(v) = elem.attr(attr) {
1597            match v.parse() {
1598                Ok(v) => {
1599                    self.register_version(ns_id, v);
1600                    Ok(Some(v))
1601                }
1602                Err(e) => Err(parser.fail(&format!("Invalid `{attr}` attribute: {e}"))),
1603            }
1604        } else {
1605            Ok(None)
1606        }
1607    }
1608
1609    fn read_object_c_type<'a>(
1610        &mut self,
1611        parser: &XmlParser<'_>,
1612        elem: &'a Element,
1613    ) -> Result<&'a str, String> {
1614        elem.attr("type")
1615            .or_else(|| elem.attr("type-name"))
1616            .ok_or_else(|| {
1617                parser.fail(&format!(
1618                    "Missing `c:type`/`glib:type-name` attributes on element <{}>",
1619                    elem.name()
1620                ))
1621            })
1622    }
1623}
1624
1625fn make_file_name(dir: &Path, name: &str) -> PathBuf {
1626    let mut path = dir.to_path_buf();
1627    let name = format!("{name}.gir");
1628    path.push(name);
1629    path
1630}