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 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 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 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}