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}