1
2use std::{io, error};
3use std::collections::{HashSet, HashMap};
4use xml;
5
6fn find_attr<'a>(a: &'a Vec<xml::attribute::OwnedAttribute>, n: &str) -> Result<&'a str, Box<dyn error::Error>> {
7 a.into_iter()
8 .find(|q| q.name.prefix.is_none() && q.name.local_name == n)
9 .map(|f| &*f.value)
10 .ok_or_else(|| format!("attribute not found: {:?}", n).into())
11}
12
13#[derive(Copy, Clone, Eq, PartialEq, Debug)]
15pub enum ServerAccess {
16 RefClosure,
18 AsRefClosure,
20 MethodInfo
22}
23
24
25#[derive(Copy, Clone, Eq, PartialEq, Debug)]
26pub enum ConnectionType {
27 Ffidisp,
28 Blocking,
29 Nonblock,
30}
31
32#[derive(Clone, Debug)]
34pub struct GenOpts {
35 pub dbuscrate: String,
37 pub methodtype: Option<String>,
39 pub crossroads: bool,
41 pub skipprefix: Option<String>,
43 pub serveraccess: ServerAccess,
45 pub genericvariant: bool,
47 pub connectiontype: ConnectionType,
49 pub propnewtype: bool,
51 pub interfaces: Option<HashSet<String>>,
53 pub command_line: String,
55}
56
57impl ::std::default::Default for GenOpts {
58 fn default() -> Self { GenOpts {
59 dbuscrate: "dbus".into(), methodtype: Some("MTFn".into()), skipprefix: None,
60 serveraccess: ServerAccess::RefClosure, genericvariant: false,
61 connectiontype: ConnectionType::Blocking, propnewtype: false,
62 interfaces: None, crossroads: false,
63 command_line: String::new()
64 }}
65}
66
67mod types;
68
69mod write;
70
71use types::*;
72
73
74pub fn generate(xmldata: &str, opts: &GenOpts) -> Result<String, Box<dyn error::Error>> {
76 use xml::EventReader;
77 use xml::reader::XmlEvent;
78
79 let mut s = String::new();
80 write::module_header(&mut s, opts);
81 let mut curintf = None;
82 let mut curm = None;
83 let mut cursig = None;
84 let mut curprop = None;
85 let mut curarg = None;
86 let parser = EventReader::new(io::Cursor::new(xmldata));
87 for e in parser {
88 match e? {
89 XmlEvent::StartElement { ref name, .. } if name.prefix.is_some() => (),
90 XmlEvent::EndElement { ref name, .. } if name.prefix.is_some() => (),
91 XmlEvent::StartElement { ref name, ref attributes, .. } if &name.local_name == "interface" => {
92 if curm.is_some() { Err("Start of Interface inside method")? };
93 if curintf.is_some() { Err("Start of Interface inside interface")? };
94 let n = find_attr(attributes, "name")?;
95 let mut n2 = n;
96 if let &Some(ref p) = &opts.skipprefix {
97 if n.len() > p.len() && n.starts_with(p) { n2 = &n[p.len()..]; }
98 }
99 curintf = Some(Intf { origname: n.into(), shortname: n2.into(),
100 methods: Vec::new(), signals: Vec::new(), props: Vec::new(), annotations: HashMap::new() });
101 }
102 XmlEvent::EndElement { ref name } if &name.local_name == "interface" => {
103 if curm.is_some() { Err("End of Interface inside method")? };
104 if curintf.is_none() { Err("End of Interface outside interface")? };
105 let intf = curintf.take().unwrap();
106 if let Some(filter) = &opts.interfaces {
108 if !filter.contains(&intf.shortname) && !filter.contains(&intf.origname) {
109 eprintln!("Skip filtered interface '{}'", &intf.shortname);
110 continue;
111 }
112 }
113 write::intf(&mut s, &intf, opts)?;
114 write::signals(&mut s, &intf)?;
115 if opts.propnewtype {
116 write::intf_name(&mut s, &intf)?;
117 write::prop_struct(&mut s, &intf)?;
118 }
119 if opts.crossroads {
120 write::intf_cr(&mut s, &intf)?;
121 }
122 if let Some(ref mt) = opts.methodtype {
123 write::intf_tree(&mut s, &intf, &mt, opts.serveraccess, opts.genericvariant)?;
124 } else if !opts.crossroads {
125 write::intf_client(&mut s, &intf, opts)?;
126 }
127 }
128
129 XmlEvent::StartElement { ref name, ref attributes, .. } if &name.local_name == "method" => {
130 if curm.is_some() { Err("Start of method inside method")? };
131 if curintf.is_none() { Err("Start of method outside interface")? };
132 let name = find_attr(attributes, "name")?;
133 curm = Some(Method { name: name.into(), fn_name: make_fn_name(curintf.as_ref().unwrap(), name),
134 iargs: Vec::new(), oargs: Vec::new(), annotations: HashMap::new() });
135 }
136 XmlEvent::EndElement { ref name } if &name.local_name == "method" => {
137 if curm.is_none() { Err("End of method outside method")? };
138 if curintf.is_none() { Err("End of method outside interface")? };
139 curintf.as_mut().unwrap().methods.push(curm.take().unwrap());
140 }
141
142 XmlEvent::StartElement { ref name, ref attributes, .. } if &name.local_name == "signal" => {
143 if cursig.is_some() { Err("Start of signal inside signal")? };
144 if curintf.is_none() { Err("Start of signal outside interface")? };
145 cursig = Some(Signal { name: find_attr(attributes, "name")?.into(), args: Vec::new(), annotations: HashMap::new() });
146 }
147 XmlEvent::EndElement { ref name } if &name.local_name == "signal" => {
148 if cursig.is_none() { Err("End of signal outside signal")? };
149 if curintf.is_none() { Err("End of signal outside interface")? };
150 curintf.as_mut().unwrap().signals.push(cursig.take().unwrap());
151 }
152
153 XmlEvent::StartElement { ref name, ref attributes, .. } if &name.local_name == "property" => {
154 if curprop.is_some() { Err("Start of property inside property")? };
155 if curintf.is_none() { Err("Start of property outside interface")? };
156 let name = find_attr(attributes, "name")?;
157 let get_fn_name = make_fn_name(curintf.as_ref().unwrap(), name);
158 let set_fn_name = make_fn_name(curintf.as_ref().unwrap(), &format!("Set{}", name));
159 curprop = Some(Prop {
160 name: name.into(),
161 typ: find_attr(attributes, "type")?.into(),
162 access: find_attr(attributes, "access")?.into(),
163 get_fn_name: get_fn_name,
164 set_fn_name: set_fn_name,
165 annotations: HashMap::new(),
166 });
167 }
168 XmlEvent::EndElement { ref name } if &name.local_name == "property" => {
169 if curprop.is_none() { Err("End of property outside property")? };
170 if curintf.is_none() { Err("End of property outside interface")? };
171 curintf.as_mut().unwrap().props.push(curprop.take().unwrap());
172 }
173
174
175 XmlEvent::StartElement { ref name, ref attributes, .. } if &name.local_name == "arg" => {
176 if curm.is_none() && cursig.is_none() { Err("Start of arg outside method and signal")? };
177 if curintf.is_none() { Err("Start of arg outside interface")? };
178 let typ = find_attr(attributes, "type")?.into();
179 let is_out = match find_attr(attributes, "direction") {
180 Err(_) => false,
181 Ok("in") => false,
182 Ok("out") => true,
183 _ => { Err("Invalid direction")?; unreachable!() }
184 };
185 let no_refs = is_out || cursig.is_some() || opts.crossroads;
186 curarg = Some(Arg { name: find_attr(attributes, "name").unwrap_or("").into(),
187 typ: typ, no_refs, idx: 0, is_out, annotations: HashMap::new() });
188 }
189
190 XmlEvent::EndElement { ref name } if &name.local_name == "arg" => {
191 if curarg.is_none() { Err("End of arg outside arg")? };
192 let arg = curarg.as_mut().unwrap();
193 let arr = if let Some(ref mut sig) = cursig { &mut sig.args }
194 else if arg.is_out { &mut curm.as_mut().unwrap().oargs } else { &mut curm.as_mut().unwrap().iargs };
195 arg.idx = arr.len() as i32;
196 arr.push(curarg.take().unwrap());
197 }
198
199 XmlEvent::StartElement { ref name, ref attributes, ..} if &name.local_name == "annotation" => {
200 if let Ok(key) = find_attr(attributes, "name") {
201 if let Ok(value) = find_attr(attributes, "value") {
202 if let Some(ref mut arg) = curarg { arg.annotations.insert(key.into(), value.into()); }
203 else if let Some(ref mut sig) = cursig { sig.annotations.insert(key.into(), value.into()); }
204 else if let Some(ref mut prop) = curprop { prop.annotations.insert(key.into(), value.into()); }
205 else if let Some(ref mut met) = curm { met.annotations.insert(key.into(), value.into()); }
206 else if let Some(ref mut intf) = curintf { intf.annotations.insert(key.into(), value.into()); }
207 }
208 }
209 }
210 _ => (),
211 }
212 }
213 if curintf.is_some() { Err("Unterminated interface")? }
214 Ok(s)
215}
216
217#[cfg(test)]
218mod tests {
219
220use super::{generate, GenOpts};
221
222static FROM_DBUS: &'static str = r#"
223<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
224"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
225<node>
226 <interface name="org.freedesktop.DBus">
227 <method name="Hello">
228 <arg direction="out" type="s"/>
229 </method>
230 <method name="RequestName">
231 <arg direction="in" type="s"/>
232 <arg direction="in" type="u"/>
233 <arg direction="out" type="u"/>
234 </method>
235 <method name="ReleaseName">
236 <arg direction="in" type="s"/>
237 <arg direction="out" type="u"/>
238 </method>
239 <method name="StartServiceByName">
240 <arg direction="in" type="s"/>
241 <arg direction="in" type="u"/>
242 <arg direction="out" type="u"/>
243 </method>
244 <method name="UpdateActivationEnvironment">
245 <arg direction="in" type="a{ss}"/>
246 </method>
247 <method name="NameHasOwner">
248 <arg direction="in" type="s"/>
249 <arg direction="out" type="b"/>
250 </method>
251 <method name="ListNames">
252 <arg direction="out" type="as"/>
253 </method>
254 <method name="ListActivatableNames">
255 <arg direction="out" type="as"/>
256 </method>
257 <method name="AddMatch">
258 <arg direction="in" type="s"/>
259 </method>
260 <method name="RemoveMatch">
261 <arg direction="in" type="s"/>
262 </method>
263 <method name="GetNameOwner">
264 <arg direction="in" type="s"/>
265 <arg direction="out" type="s"/>
266 </method>
267 <method name="ListQueuedOwners">
268 <arg direction="in" type="s"/>
269 <arg direction="out" type="as"/>
270 </method>
271 <method name="GetConnectionUnixUser">
272 <arg direction="in" type="s"/>
273 <arg direction="out" type="u"/>
274 </method>
275 <method name="GetConnectionUnixProcessID">
276 <arg direction="in" type="s"/>
277 <arg direction="out" type="u"/>
278 </method>
279 <method name="GetAdtAuditSessionData">
280 <arg direction="in" type="s"/>
281 <arg direction="out" type="ay"/>
282 </method>
283 <method name="GetConnectionSELinuxSecurityContext">
284 <arg direction="in" type="s"/>
285 <arg direction="out" type="ay"/>
286 </method>
287 <method name="GetConnectionAppArmorSecurityContext">
288 <arg direction="in" type="s"/>
289 <arg direction="out" type="s"/>
290 </method>
291 <method name="ReloadConfig">
292 </method>
293 <method name="GetId">
294 <arg direction="out" type="s"/>
295 </method>
296 <method name="GetConnectionCredentials">
297 <arg direction="in" type="s"/>
298 <arg direction="out" type="a{sv}"/>
299 </method>
300 <signal name="NameOwnerChanged">
301 <arg type="s"/>
302 <arg type="s"/>
303 <arg type="s"/>
304 </signal>
305 <signal name="NameLost">
306 <arg type="s"/>
307 </signal>
308 <signal name="NameAcquired">
309 <arg type="s"/>
310 </signal>
311 </interface>
312 <interface name="org.freedesktop.DBus.Introspectable">
313 <method name="Introspect">
314 <arg direction="out" type="s"/>
315 </method>
316 </interface>
317 <interface name="org.freedesktop.DBus.Monitoring">
318 <method name="BecomeMonitor">
319 <arg direction="in" type="as"/>
320 <arg direction="in" type="u"/>
321 </method>
322 </interface>
323 <interface name="org.freedesktop.DBus.Debug.Stats">
324 <method name="GetStats">
325 <arg direction="out" type="a{sv}"/>
326 </method>
327 <method name="GetConnectionStats">
328 <arg direction="in" type="s"/>
329 <arg direction="out" type="a{sv}"/>
330 </method>
331 <method name="GetAllMatchRules">
332 <arg direction="out" type="a{sas}"/>
333 </method>
334 </interface>
335</node>
336"#;
337
338 #[test]
339 fn from_dbus() {
340 let s = generate(FROM_DBUS, &GenOpts { methodtype: Some("MTSync".into()), ..Default::default() }).unwrap();
341 println!("{}", s);
342 }
344}