gir_parser/
lib.rs

1#![doc = include_str!("../README.md")]
2use xmlserde::xml_serde_enum;
3
4#[derive(Debug)]
5pub enum ParserError {
6    IO(std::io::Error),
7    Xml(String),
8}
9
10impl From<std::io::Error> for ParserError {
11    fn from(value: std::io::Error) -> Self {
12        Self::IO(value)
13    }
14}
15
16impl std::error::Error for ParserError {}
17impl std::fmt::Display for ParserError {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            Self::IO(e) => f.write_fmt(format_args!("I/O operation failed {e}")),
21            Self::Xml(e) => f.write_fmt(format_args!("Failed to parse xml file: {e}")),
22        }
23    }
24}
25
26xml_serde_enum! {
27    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
28    Stability {
29        Stable => "Stable",
30        Unstable => "Unstable",
31        Private => "Private",
32    }
33}
34
35xml_serde_enum! {
36    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
37    TransferOwnership {
38        None => "none",
39        Container => "container",
40        Full => "full",
41    }
42}
43
44impl TransferOwnership {
45    pub fn is_none(&self) -> bool {
46        matches!(self, Self::None)
47    }
48
49    pub fn is_full(&self) -> bool {
50        matches!(self, Self::Full)
51    }
52
53    pub fn is_container(&self) -> bool {
54        matches!(self, Self::Container)
55    }
56}
57
58xml_serde_enum! {
59    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
60    FunctionScope {
61        Call => "call",
62        Notified => "notified",
63        Async => "async",
64        Forever => "forever",
65    }
66}
67
68impl FunctionScope {
69    pub fn is_call(&self) -> bool {
70        matches!(self, Self::Call)
71    }
72
73    pub fn is_notified(&self) -> bool {
74        matches!(self, Self::Notified)
75    }
76
77    pub fn is_async(&self) -> bool {
78        matches!(self, Self::Async)
79    }
80
81    pub fn is_forever(&self) -> bool {
82        matches!(self, Self::Forever)
83    }
84}
85
86xml_serde_enum! {
87    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
88    SignalEmission {
89        First => "first",
90        Last => "last",
91        Cleanup => "cleanup",
92    }
93}
94
95#[macro_use]
96mod traits;
97pub mod prelude {
98    pub use xmlserde::XmlValue;
99
100    pub use super::traits::*;
101}
102
103mod alias;
104pub use alias::Alias;
105mod array;
106pub use array::Array;
107mod attribute;
108pub use attribute::Attribute;
109mod bitfield;
110pub use bitfield::BitField;
111mod boxed;
112pub use boxed::Boxed;
113mod callback;
114pub use callback::Callback;
115mod class;
116pub use class::{Class, ClassField, Implements};
117mod constant;
118pub use constant::Constant;
119mod documentation;
120pub use documentation::{DocDeprecated, DocStability, DocVersion, Documentation, SourcePosition};
121mod enums;
122pub use enums::Enumeration;
123mod field;
124pub use field::{Field, FieldType};
125mod function;
126pub use function::{Function, FunctionInline};
127mod function_macro;
128pub use function_macro::FunctionMacro;
129mod interface;
130pub use interface::{Interface, InterfaceField, Prerequisite};
131mod member;
132pub use member::Member;
133mod method;
134pub use method::{Method, MethodInline};
135mod namespace;
136pub use namespace::Namespace;
137mod parameter;
138pub use parameter::{
139    AnyParameter, Direction, InstanceParameter, Parameter, ParameterType, Parameters,
140};
141mod property;
142pub use property::Property;
143mod record;
144pub use record::{Record, RecordField};
145mod repository;
146pub use repository::{HeaderInclude, NamespaceInclude, Package, Repository};
147mod return_value;
148pub use return_value::ReturnValue;
149mod signal;
150pub use signal::Signal;
151mod r#type;
152pub use r#type::{AnyType, Type};
153mod union;
154pub use union::{Union, UnionField};
155mod version;
156pub use version::Version;
157mod virtual_method;
158pub use virtual_method::VirtualMethod;
159
160#[cfg(test)]
161mod tests {
162    use std::{path::PathBuf, str::FromStr};
163
164    use super::prelude::*;
165
166    const GIR_FILES: [&str; 35] = [
167        "Atk-1.0",
168        "cairo-1.0",
169        "fontconfig-2.0",
170        "freetype2-2.0",
171        "Gdk-3.0",
172        "Gdk-4.0",
173        "GdkPixbuf-2.0",
174        "GdkPixdata-2.0",
175        "GdkWayland-4.0",
176        "GdkWin32-4.0",
177        "GdkX11-3.0",
178        "GdkX11-4.0",
179        "Gio-2.0",
180        "GL-1.0",
181        "GLib-2.0",
182        "GModule-2.0",
183        "GObject-2.0",
184        "Graphene-1.0",
185        "Gsk-4.0",
186        "Gtk-3.0",
187        "Gtk-4.0",
188        "HarfBuzz-0.0",
189        "libxml2-2.0",
190        "Pango-1.0",
191        "PangoCairo-1.0",
192        "PangoFc-1.0",
193        "PangoFT2-1.0",
194        "PangoOT-1.0",
195        "PangoXft-1.0",
196        "Vulkan-1.0",
197        "win32-1.0",
198        "xfixes-4.0",
199        "xft-2.0",
200        "xlib-2.0",
201        "xrandr-1.3",
202    ];
203
204    use super::{repository::Repository, version::Version};
205
206    fn parse_gir(gir_file: &str) -> Repository {
207        let path = PathBuf::from("./gir-files").join(&format!("{}.gir", gir_file));
208        Repository::from_path(path).unwrap()
209    }
210    #[test]
211    fn xft_gir() {
212        let repo = parse_gir(GIR_FILES[32]);
213        assert_eq!(repo.version(), Version::from_str("1.2").ok().as_ref());
214        assert_eq!(repo.namespace_includes()[0].as_package(), "xlib-2.0");
215
216        let namespace = repo.namespace();
217        assert_eq!(namespace.version(), "2.0");
218        assert_eq!(namespace.name(), "xft");
219        assert_eq!(
220            namespace.c_identifier_prefixes().collect::<Vec<_>>(),
221            vec!["Xft"]
222        );
223        assert_eq!(
224            namespace.c_symbol_prefixes().collect::<Vec<_>>(),
225            vec!["Xft"]
226        );
227    }
228
229    #[test]
230    fn xlib_gir() {
231        let repo = parse_gir(GIR_FILES[33]);
232        assert_eq!(repo.version(), Version::from_str("1.2").ok().as_ref());
233        let namespace = repo.namespace();
234        assert_eq!(namespace.version(), "2.0");
235        assert_eq!(namespace.name(), "xlib");
236        assert!(namespace
237            .c_identifier_prefixes()
238            .collect::<Vec<_>>()
239            .is_empty());
240        assert_eq!(namespace.c_symbol_prefixes().collect::<Vec<_>>(), vec!["X"]);
241        let aliases = namespace.aliases();
242        assert_eq!(aliases[0].name(), "Atom");
243        assert_eq!(aliases[0].c_type(), "Atom");
244        assert_eq!(aliases[0].ty().as_type().name(), Some("gulong"));
245        assert_eq!(aliases[0].ty().as_type().c_type(), Some("gulong"));
246
247        let records = namespace.records();
248        assert_eq!(records[0].name(), Some("Display"));
249        assert_eq!(records[0].c_type(), Some("Display"));
250
251        let unions = namespace.unions();
252        assert_eq!(unions[0].name(), Some("XEvent"));
253        assert_eq!(unions[0].c_type(), Some("XEvent"));
254
255        let functions = namespace.functions();
256        assert_eq!(functions[0].name(), "open_display");
257        assert_eq!(functions[0].c_identifier(), Some("XOpenDisplay"));
258        assert!(functions[0].parameters().is_empty());
259
260        let return_value = functions[0].return_value();
261        assert!(return_value.transfer_ownership().unwrap().is_none());
262        assert_eq!(return_value.ty().as_type().name(), Some("none"));
263        assert_eq!(return_value.ty().as_type().c_type(), Some("void"));
264    }
265
266    #[test]
267    fn xrandr_gir() {
268        let repo = parse_gir(GIR_FILES[34]);
269        assert_eq!(repo.version(), Version::from_str("1.2").ok().as_ref());
270        let namespace = repo.namespace();
271        assert_eq!(namespace.version(), "1.3");
272        assert_eq!(namespace.name(), "xrandr");
273        assert_eq!(
274            namespace.c_identifier_prefixes().collect::<Vec<_>>(),
275            vec!["XRR"]
276        );
277        assert_eq!(
278            namespace.c_symbol_prefixes().collect::<Vec<_>>(),
279            vec!["XRR"]
280        );
281        let records = namespace.records();
282        assert_eq!(records[0].name(), Some("ScreenSize"));
283        assert_eq!(records[0].c_type(), Some("XRRScreenSize"));
284    }
285
286    #[test]
287    fn parse_all_gir_files() {
288        let paths = std::fs::read_dir("./gir-files").unwrap();
289
290        for path in paths {
291            let path = path.unwrap().path();
292            let gir_file = path
293                .file_name()
294                .unwrap()
295                .to_str()
296                .unwrap()
297                .trim_end_matches(".gir")
298                .to_owned();
299            println!("{:#?}", gir_file);
300            let repository = parse_gir(&gir_file);
301            assert!(gir_file.starts_with(repository.namespace().name()));
302        }
303    }
304}