qapi_parser/
lib.rs

1#![doc(html_root_url = "https://docs.rs/qapi-parser/0.11.0")]
2
3pub mod spec {
4    use std::fmt;
5    use serde::de::{Deserializer, Visitor, SeqAccess, MapAccess, Error};
6    use serde::de::value::MapAccessDeserializer;
7    use serde::Deserialize;
8
9    #[derive(Debug, Clone, Deserialize)]
10    #[serde(untagged, rename_all = "lowercase")]
11    pub enum Spec {
12        Include(Include),
13        Command(Command),
14        Struct(Struct),
15        Alternate(Alternate),
16        Enum(Enum),
17        Event(Event),
18        CombinedUnion(CombinedUnion),
19        Union(Union),
20        PragmaWhitelist {
21            pragma: PragmaWhitelist
22        },
23        PragmaExceptions {
24            pragma: PragmaExceptions
25        },
26        PragmaDocRequired {
27            pragma: PragmaDocRequired
28        },
29    }
30
31    #[derive(Debug, Default, Clone)]
32    pub struct Data {
33        pub fields: Vec<Value>,
34    }
35
36    impl Data {
37        pub fn is_empty(&self) -> bool {
38            self.fields.is_empty() || self.fields.iter().all(|f| f.optional)
39        }
40
41        pub fn newtype(&self) -> Option<&Value> {
42            match self.fields.get(0) {
43                Some(data) if self.fields.len() == 1 => Some(data),
44                _ => None,
45            }
46        }
47    }
48
49    impl<'de> Deserialize<'de> for Data {
50        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
51            use serde::de::IntoDeserializer;
52
53            let fields: serde_json::Map<String, serde_json::Value> = Deserialize::deserialize(d)?;
54            let fields = fields.into_iter().map(|(n, t)|
55                Type::deserialize(t.into_deserializer()).map(|t|
56                    Value::new(&n, t)
57                ).map_err(D::Error::custom)
58            ).collect::<Result<_, _>>()?;
59
60            Ok(Data {
61                fields,
62            })
63        }
64    }
65
66    #[derive(Debug, Clone)]
67    pub struct Value {
68        pub name: String,
69        pub ty: Type,
70        pub optional: bool,
71    }
72
73    impl Value {
74        pub fn new(name: &str, ty: Type) -> Self {
75            let (name, opt) = if name.starts_with("*") {
76                (name[1..].into(), true)
77            } else {
78                (name.into(), false)
79            };
80
81            Value {
82                name,
83                ty,
84                optional: opt,
85            }
86        }
87    }
88
89    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
90    #[serde(rename_all = "kebab-case")]
91    pub enum Feature {
92        Deprecated,
93        Unstable,
94        JsonCli,
95        JsonCliHotplug,
96        // what are these?
97        AllowWriteOnlyOverlay,
98        DynamicAutoReadOnly,
99        SavevmMonitorNodes,
100        Fdset,
101    }
102
103    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
104    #[serde(untagged)]
105    pub enum ConditionalFeature {
106        Feature(Feature),
107        Conditional {
108            name: Feature,
109            #[serde(default)]
110            conditional: Option<Conditional>,
111        },
112    }
113
114    impl PartialEq<Feature> for ConditionalFeature {
115        fn eq(&self, rhs: &Feature) -> bool {
116            match self {
117                ConditionalFeature::Feature(name) => name == rhs,
118                ConditionalFeature::Conditional { name, .. } => name == rhs,
119            }
120        }
121    }
122
123    #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
124    #[serde(transparent)]
125    pub struct Features {
126        // TODO: make this a set instead?
127        pub features: Vec<ConditionalFeature>,
128    }
129
130    impl Features {
131        pub fn is_deprecated(&self) -> bool {
132            self.features.iter().any(|f| f == &Feature::Deprecated)
133        }
134    }
135
136    #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137    pub struct Type {
138        pub name: String,
139        pub is_array: bool,
140        pub conditional: Option<Conditional>,
141        pub features: Features,
142    }
143
144    impl fmt::Debug for Type {
145        fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
146            if self.is_array {
147                write!(fmt, "[{}]", self.name)?
148            } else {
149                write!(fmt, "{}", self.name)?
150            }
151
152            if let Some(cond) = &self.conditional {
153                write!(fmt, " {:?}", cond)
154            } else {
155                Ok(())
156            }
157        }
158    }
159
160    impl<'de> Deserialize<'de> for Type {
161        fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
162            struct V;
163
164            impl<'de> Visitor<'de> for V {
165                type Value = Type;
166
167                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
168                    write!(formatter, "a Type of string or single element array")
169                }
170
171                fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
172                    Ok(Type {
173                        name: v.into(),
174                        is_array: false,
175                        conditional: None,
176                        features: Default::default(),
177                    })
178                }
179
180                fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
181                    #[derive(Debug, Clone, Deserialize)]
182                    struct ConditionalType {
183                        #[serde(rename = "type")]
184                        ty: Type,
185                        #[serde(default, rename = "if")]
186                        conditional: Option<Conditional>,
187                        #[serde(default)]
188                        features: Features,
189                    }
190
191                    let ty = ConditionalType::deserialize(MapAccessDeserializer::new(map))?;
192                    Ok(Type {
193                        conditional: ty.conditional,
194                        features: ty.features,
195                        .. ty.ty
196                    })
197                }
198
199                fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
200                    Ok(Type {
201                        name: v,
202                        is_array: false,
203                        conditional: None,
204                        features: Default::default(),
205                    })
206                }
207
208                fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
209                    let v = seq.next_element::<String>()?;
210                    if let Some(v) = v {
211                        if seq.next_element::<String>()?.is_none() {
212                            Ok(Type {
213                                name: v,
214                                is_array: true,
215                                conditional: None,
216                                features: Default::default(),
217                            })
218                        } else {
219                            Err(A::Error::invalid_length(2, &"single array item"))
220                        }
221                    } else {
222                        Err(A::Error::invalid_length(0, &"single array item"))
223                    }
224                }
225            }
226
227            d.deserialize_any(V)
228        }
229    }
230
231    /// A #define'd symbol such as `CONFIG_SPICE`
232    pub type ConditionalDefinition = String;
233
234    #[derive(Debug, Clone, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash)]
235    #[serde(untagged, rename_all = "kebab-case")]
236    pub enum Conditional {
237        Define(ConditionalDefinition),
238        Not {
239            not: ConditionalDefinition,
240        },
241        All {
242            all: Vec<ConditionalDefinition>,
243        },
244        Any {
245            any: Vec<ConditionalDefinition>,
246        },
247    }
248
249    #[derive(Debug, Clone, Deserialize)]
250    #[serde(rename_all = "kebab-case")]
251    pub struct Include {
252        pub include: String,
253    }
254
255    #[derive(Debug, Clone, Deserialize)]
256    #[serde(rename_all = "kebab-case")]
257    pub struct Command {
258        #[serde(rename = "command")]
259        pub id: String,
260        #[serde(default)]
261        pub data: DataOrType,
262        #[serde(default)]
263        pub returns: Option<Type>,
264        #[serde(default, rename = "if")]
265        pub conditional: Option<Conditional>,
266        #[serde(default)]
267        pub allow_oob: bool,
268        #[serde(default)]
269        pub coroutine: bool,
270        #[serde(default)]
271        pub boxed: bool,
272        #[serde(default = "Command::success_response_default")]
273        pub success_response: bool,
274        #[serde(default)]
275        pub allow_preconfig: bool,
276        #[serde(default)]
277        pub features: Features,
278        #[serde(default = "Command::gen_default")]
279        pub gen: bool,
280    }
281
282    impl Command {
283        fn success_response_default() -> bool { true }
284        fn gen_default() -> bool { true }
285    }
286
287    #[derive(Debug, Clone, Deserialize)]
288    #[serde(rename_all = "kebab-case")]
289    pub struct Struct {
290        #[serde(rename = "struct")]
291        pub id: String,
292        #[serde(default)]
293        pub data: Data,
294        #[serde(default)]
295        pub base: DataOrType,
296        #[serde(default, rename = "if")]
297        pub conditional: Option<Conditional>,
298        #[serde(default)]
299        pub features: Features,
300    }
301
302    impl Struct {
303        pub fn newtype(&self) -> Option<&Value> {
304            match &self.base {
305                DataOrType::Data(d) if d.fields.is_empty() => (),
306                _ => return None,
307            }
308            self.data.newtype()
309        }
310
311        pub fn wrapper_type(&self) -> Option<&Value> {
312            match self.id.ends_with("Wrapper") {
313                true => self.newtype(),
314                false => None,
315            }
316        }
317
318        pub fn is_empty(&self) -> bool {
319            self.base.is_empty() && self.data.is_empty()
320        }
321    }
322
323    #[derive(Debug, Clone, Deserialize)]
324    #[serde(rename_all = "kebab-case")]
325    pub struct Alternate {
326        #[serde(rename = "alternate")]
327        pub id: String,
328        #[serde(default)]
329        pub data: Data,
330        #[serde(default, rename = "if")]
331        pub conditional: Option<Conditional>,
332    }
333
334    #[derive(Debug, Clone, Deserialize)]
335    #[serde(rename_all = "kebab-case")]
336    pub struct Enum {
337        #[serde(rename = "enum")]
338        pub id: String,
339        #[serde(default)]
340        pub data: Vec<SpecName>,
341        #[serde(default)]
342        pub prefix: Option<String>,
343        #[serde(default, rename = "if")]
344        pub conditional: Option<Conditional>,
345    }
346
347    #[derive(Debug, Clone, Deserialize)]
348    #[serde(rename_all = "kebab-case")]
349    pub struct CombinedUnion {
350        #[serde(rename = "union")]
351        pub id: String,
352        pub base: DataOrType,
353        #[serde(default)]
354        pub discriminator: Option<String>,
355        pub data: Data,
356        #[serde(default, rename = "if")]
357        pub conditional: Option<Conditional>,
358    }
359
360    #[derive(Debug, Clone, Deserialize)]
361    #[serde(rename_all = "kebab-case")]
362    pub struct Union {
363        #[serde(rename = "union")]
364        pub id: String,
365        #[serde(default)]
366        pub discriminator: Option<String>,
367        pub data: Data,
368        #[serde(default, rename = "if")]
369        pub conditional: Option<Conditional>,
370    }
371
372    #[derive(Debug, Clone, Deserialize)]
373    #[serde(untagged, rename_all = "kebab-case")]
374    pub enum DataOrType {
375        Data(Data),
376        Type(Type),
377    }
378
379    impl Default for DataOrType {
380        fn default() -> Self {
381            DataOrType::Data(Default::default())
382        }
383    }
384
385    impl DataOrType {
386        pub fn is_empty(&self) -> bool {
387            match self {
388                &DataOrType::Data(ref data) => data.fields.is_empty(),
389                &DataOrType::Type(..) => false,
390            }
391        }
392
393        pub fn len(&self) -> usize {
394            match self {
395                &DataOrType::Data(ref data) => data.fields.len(),
396                &DataOrType::Type(..) => 1,
397            }
398        }
399    }
400
401    #[derive(Debug, Clone, Deserialize)]
402    #[serde(rename_all = "kebab-case")]
403    pub struct Event {
404        #[serde(rename = "event")]
405        pub id: String,
406        #[serde(default)]
407        pub data: DataOrType,
408        #[serde(default, rename = "if")]
409        pub conditional: Option<Conditional>,
410        #[serde(default)]
411        pub features: Features,
412    }
413
414    #[derive(Debug, Clone, Deserialize)]
415    #[serde(rename_all = "kebab-case")]
416    pub struct PragmaWhitelist {
417        pub returns_whitelist: Vec<String>,
418        #[serde(default)]
419        pub name_case_whitelist: Vec<String>,
420    }
421
422    #[derive(Debug, Clone, Deserialize)]
423    #[serde(rename_all = "kebab-case")]
424    pub struct PragmaExceptions {
425        pub command_returns_exceptions: Vec<String>,
426        #[serde(default)]
427        pub member_name_exceptions: Vec<String>,
428    }
429
430    #[derive(Debug, Clone, Deserialize)]
431    #[serde(rename_all = "kebab-case")]
432    pub struct PragmaDocRequired {
433        pub doc_required: bool,
434    }
435
436    #[derive(Debug, Clone, Deserialize)]
437    #[serde(untagged, rename_all = "kebab-case")]
438    pub enum SpecName {
439        Name(String),
440        Conditional {
441            name: String,
442            #[serde(rename = "if")]
443            conditional: Conditional,
444            #[serde(default)]
445            features: Features,
446        },
447        Explicit {
448            name: String,
449            #[serde(default)]
450            features: Features,
451        },
452    }
453
454    impl SpecName {
455        pub fn name(&self) -> &String {
456            match self {
457                SpecName::Name(name) | SpecName::Explicit { name, .. } => name,
458                SpecName::Conditional { name, .. } => name,
459            }
460        }
461
462        pub fn features(&self) -> Option<&Features> {
463            match self {
464                SpecName::Name(..) => None,
465                SpecName::Explicit { features, .. } => Some(features),
466                SpecName::Conditional { features, .. } => Some(features),
467            }
468        }
469    }
470
471    impl fmt::Display for SpecName {
472        fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
473            fmt::Display::fmt(self.as_ref(), fmt)
474        }
475    }
476    impl AsRef<str> for SpecName {
477        fn as_ref(&self) -> &str {
478            &self.name()[..]
479        }
480    }
481}
482
483pub use self::spec::Spec;
484
485use std::path::{Path, PathBuf};
486use std::ops::{Deref, DerefMut};
487use std::io;
488
489pub struct Parser {
490    data: String,
491    pos: usize,
492    eof: bool,
493}
494
495impl Parser {
496    pub fn from_string<S: Into<String>>(s: S) -> Self {
497        Parser {
498            data: s.into(),
499            pos: 0,
500            eof: false,
501        }
502    }
503
504    pub fn strip_comments(s: &str) -> String {
505        let lines: Vec<String> = s.lines()
506            .filter(|l| !l.trim().starts_with("#") && !l.trim().is_empty())
507            .map(|s| s.replace("'", "\""))
508            .map(|s| if let Some(i) = s.find('#') {
509                s[..i].to_owned()
510            } else {
511                s
512            }).collect();
513        lines.join("\n")
514    }
515}
516
517impl Iterator for Parser {
518    type Item = serde_json::Result<Spec>;
519
520    fn next(&mut self) -> Option<Self::Item> {
521        if self.eof {
522            None
523        } else {
524            Some(match serde_json::from_str(&self.data[self.pos..]) {
525                Ok(res) => {
526                    self.eof = true;
527                    Ok(res)
528                },
529                Err(e) => {
530                    let (line, col) = (e.line(), e.column());
531                    if line == 0 || col == 0 {
532                        Err(e)
533                    } else {
534                        let count: usize =  self.data[self.pos..].lines().map(|l| l.len() + 1).take(line - 1).sum();
535                        let str = &self.data[self.pos .. (self.pos + count + col - 1)];
536                        self.pos += count;
537                        serde_json::from_str(str)
538                    }
539                },
540            })
541        }
542    }
543}
544
545pub trait QemuRepo {
546    type Error;
547
548    fn push_context<P: AsRef<Path>>(&mut self, p: P);
549    fn pop_context(&mut self);
550    fn context(&self) -> &Path;
551
552    fn include<P: AsRef<Path>>(&mut self, p: P) -> Result<(QemuRepoContext<Self>, String), Self::Error>;
553}
554
555#[derive(Debug, Clone)]
556pub struct QemuFileRepo {
557    paths: Vec<PathBuf>,
558}
559
560pub struct QemuRepoContext<'a, R: QemuRepo + ?Sized + 'a> {
561    repo: &'a mut R,
562}
563
564impl<'a, R: QemuRepo + ?Sized + 'a> QemuRepoContext<'a, R> {
565    pub fn from_include<P: AsRef<Path>>(repo: &'a mut R, path: P) -> (Self, PathBuf) {
566        let path = path.as_ref();
567        let include_path = repo.context().join(path);
568        repo.push_context(include_path.parent().unwrap());
569
570        (
571            QemuRepoContext {
572                repo,
573            },
574            include_path,
575        )
576    }
577}
578
579impl<'a, R: QemuRepo + ?Sized + 'a> Deref for QemuRepoContext<'a, R> {
580    type Target = R;
581
582    fn deref(&self) -> &Self::Target {
583        self.repo
584    }
585}
586
587impl<'a, R: QemuRepo + ?Sized + 'a> DerefMut for QemuRepoContext<'a, R> {
588    fn deref_mut(&mut self) -> &mut Self::Target {
589        self.repo
590    }
591}
592
593impl<'a, R: QemuRepo + ?Sized + 'a> Drop for QemuRepoContext<'a, R> {
594    fn drop(&mut self) {
595        self.repo.pop_context();
596    }
597}
598
599impl QemuFileRepo {
600    pub fn new<P: Into<PathBuf>>(p: P) -> Self {
601        QemuFileRepo {
602            paths: vec![p.into()],
603        }
604    }
605}
606
607impl QemuRepo for QemuFileRepo {
608    type Error = io::Error;
609
610    fn push_context<P: AsRef<Path>>(&mut self, p: P) {
611        self.paths.push(p.as_ref().to_owned());
612    }
613
614    fn pop_context(&mut self) {
615        self.paths.pop();
616        assert!(!self.paths.is_empty());
617    }
618
619    fn context(&self) -> &Path {
620        self.paths.last().unwrap()
621    }
622
623    fn include<P: AsRef<Path>>(&mut self, p: P) -> Result<(QemuRepoContext<Self>, String), Self::Error> {
624        use std::fs::File;
625        use std::io::Read;
626
627        let (context, path) = QemuRepoContext::from_include(self, p);
628        let mut f = File::open(path)?;
629        let mut str = String::new();
630        f.read_to_string(&mut str)?;
631        Ok((context, str))
632    }
633}
634
635#[cfg(test)]
636mod test {
637    use super::*;
638    use std::path::Path;
639
640    fn parse_include<P: AsRef<Path>>(repo: &mut QemuFileRepo, include: P) {
641        let include = include.as_ref();
642        println!("including {}", include.display());
643
644        let (mut context, schema) = repo.include(include).expect("include path not found");
645        for item in Parser::from_string(Parser::strip_comments(&schema)) {
646            match item.expect("schema parse failure") {
647                Spec::Include(inc) => parse_include(&mut context, inc.include),
648                item => println!("decoded {:?}", item),
649            }
650        }
651    }
652
653    fn parse_schema(mut repo: QemuFileRepo) {
654        parse_include(&mut repo, "qapi-schema.json");
655    }
656
657    #[test]
658    fn parse_qapi() {
659        parse_schema(QemuFileRepo::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../schema/qapi/")));
660    }
661
662    #[test]
663    fn parse_qga() {
664        parse_schema(QemuFileRepo::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../schema/qga/")));
665    }
666}