qapi_codegen/
lib.rs

1#![doc(html_root_url = "https://docs.rs/qapi-codegen/0.11.3")]
2
3//! Generates Rust types for the [QAPI schema language](https://qemu-project.gitlab.io/qemu/devel/qapi-code-gen.html#the-qapi-schema-language)
4
5use qapi_parser::{Parser, QemuFileRepo, QemuRepo, spec};
6use qapi_parser::spec::Spec;
7use std::collections::{BTreeMap, HashSet};
8use std::path::{Path, PathBuf};
9use std::fs::File;
10use std::io::{self, Write};
11use std::mem::replace;
12
13// kebab-case to PascalCase?
14fn type_identifier<S: AsRef<str>>(id: S) -> String {
15    identifier(id)
16}
17
18// kebab-case to snake_case
19fn identifier<S: AsRef<str>>(id: S) -> String {
20    let id = id.as_ref();
21    match id {
22        "type" | "static" | "virtual" | "abstract" | "in" | "if" | "enum" | "match" | "use" => format!("{}_", id),
23        s if s.as_bytes()[0].is_ascii_digit() => format!("_{}", s),
24        id => id.replace("-", "_")
25    }
26}
27
28// SCREAMING_SNAKE_CASE to PascalCase?
29fn event_identifier(id: &str) -> String {
30    id.into()
31}
32
33// no case change, just check for rust primitives
34fn typename_s(ty: &str) -> String {
35    match ty {
36        "str" => "::std::string::String".into(),
37        "any" => "::qapi_spec::Any".into(),
38        "null" => "()".into(),
39        "number" => "f64".into(),
40        "int8" => "i8".into(),
41        "uint8" => "u8".into(),
42        "int16" => "i16".into(),
43        "uint16" => "u16".into(),
44        "int32" => "i32".into(),
45        "uint32" => "u32".into(),
46        "int64" => "i64".into(),
47        "uint64" => "u64".into(),
48        "size" => "u64".into(),
49        "int" => "i64".into(),
50        ty => ty.into(),
51    }
52}
53
54fn type_attrs(ty: &spec::Type) -> String {
55    feature_attrs(&ty.features)
56}
57
58fn feature_attrs(ty: &spec::Features) -> String {
59    if ty.is_deprecated() { " #[deprecated]".into() } else { String::new() }
60}
61
62fn typename(ty: &spec::Type) -> String {
63    if ty.is_array {
64        format!("Vec<{}>", typename_s(&ty.name))
65    } else {
66        typename_s(&ty.name)
67    }
68}
69
70fn valuety(value: &spec::Value, pubvis: bool, super_name: &str) -> String {
71    // overrides for recursive types:
72    let boxed = value.ty.name == super_name;
73
74    let base64 = value.ty.name == "str" && (
75        ((super_name == "GuestFileRead" || super_name == "guest-file-write") && value.name == "buf-b64") ||
76        (super_name == "guest-set-user-password" && value.name == "password") ||
77        (super_name == "GuestExecStatus" && (value.name == "out-data" || value.name == "err-data")) ||
78        (super_name == "guest-exec" && value.name == "input-data") ||
79        (super_name == "get-win32-socket" && value.name == "info") ||
80        (super_name == "SevGuestProperties" && (value.name == "dh-cert-file" || value.name == "session-file")) ||
81        (super_name == "SecretCommonProperties" && value.name == "iv")
82        // `ringbuf-write`, `ringbuf-read`, `SecretProperties` can't be done because weird enums
83    );
84
85    let dict = value.ty.name == "any" && (
86        (super_name == "object-add" && value.name == "props") ||
87        (super_name == "CpuModelInfo" && value.name == "props")
88    );
89
90    // TODO: handle optional Vec<>s specially?
91
92    let ty = typename(&value.ty);
93    let (attr, ty) = if base64 {
94        let ty = "Vec<u8>".into();
95        if value.optional {
96            (", with = \"::qapi_spec::base64_opt\"", ty)
97        } else {
98            (", with = \"::qapi_spec::base64\"", ty)
99        }
100    } else if boxed {
101        ("", format!("Box<{}>", ty))
102    } else if dict {
103        ("", "::qapi_spec::Dictionary".into())
104    } else if super_name == "guest-shutdown" && value.name == "mode" {
105        ("", "GuestShutdownMode".into())
106    } else {
107        ("", ty)
108    };
109
110    let (attr, ty) = if value.optional {
111        (format!("{}, default, skip_serializing_if = \"Option::is_none\"", attr), format!("Option<{}>", ty))
112    } else {
113        (attr.into(), ty)
114    };
115
116    format!("#[serde(rename = \"{}\"{})]{}\n{}{}: {}",
117        value.name,
118        attr,
119        type_attrs(&value.ty),
120        if pubvis { "pub " } else { "" },
121        identifier(&value.name),
122        ty
123    )
124}
125
126struct Context<W> {
127    includes: Vec<String>,
128    included: HashSet<PathBuf>,
129    events: Vec<spec::Event>,
130    unions: BTreeMap<String, spec::CombinedUnion>,
131    enums: BTreeMap<String, spec::Enum>,
132    types: BTreeMap<String, spec::Struct>,
133    struct_discriminators: BTreeMap<String, String>,
134    command_trait: String,
135    out: W,
136}
137
138impl<W: Write> Context<W> {
139    fn new(out: W, command_trait: String) -> Self {
140        Context {
141            includes: Default::default(),
142            included: Default::default(),
143            events: Default::default(),
144            unions: Default::default(),
145            enums: Default::default(),
146            types: Default::default(),
147            struct_discriminators: Default::default(),
148            command_trait,
149            out,
150        }
151    }
152
153    fn process(&mut self, item: spec::Spec) -> io::Result<()> {
154        match item {
155            Spec::Include(include) => {
156                self.includes.push(include.include);
157            },
158            Spec::Command(v) => {
159                let type_id = type_identifier(&v.id);
160                match v.data {
161                    spec::DataOrType::Type(ref ty) if type_identifier(&ty.name) == type_id => (),
162                    ty => {
163                        write!(self.out, "
164#[derive(Debug, Clone, Serialize, Deserialize)]{}
165pub struct {}", feature_attrs(&v.features), type_id)?;
166                        match ty {
167                            spec::DataOrType::Data(ref data) => {
168                                writeln!(self.out, " {{")?;
169                                for data in &data.fields {
170                                    writeln!(self.out, "\t{},", valuety(&data, true, &v.id))?;
171                                }
172                                if !v.gen {
173                                    writeln!(self.out, "
174    #[serde(flatten)]
175    pub arguments: ::qapi_spec::Dictionary,
176")?;
177                                }
178                                writeln!(self.out, "}}")?;
179                            },
180                            spec::DataOrType::Type(ref ty) => {
181                                let ty_name = type_identifier(&ty.name);
182                                writeln!(self.out, "({}pub {});", type_attrs(ty), ty_name)?;
183                                writeln!(self.out, "
184impl From<{}> for {} {{
185    fn from(val: {}) -> Self {{
186        Self(val)
187    }}
188}}
189", ty_name, type_id, ty_name)?;
190                            },
191                        }
192                    },
193                }
194
195                write!(self.out, "
196impl crate::{} for {} {{ }}
197impl ::qapi_spec::Command for {} {{
198    const NAME: &'static str = \"{}\";
199    const ALLOW_OOB: bool = {};
200
201    type Ok = ", self.command_trait, type_id, type_id, v.id, v.allow_oob)?;
202                if let Some(ret) = v.returns {
203                    writeln!(self.out, "{};", typename(&ret))
204                } else {
205                    writeln!(self.out, "::qapi_spec::Empty;")
206                }?;
207                writeln!(self.out, "}}")?;
208            },
209            Spec::Struct(v) => {
210                self.types.insert(v.id.clone(), v);
211            },
212            Spec::Alternate(v) => {
213                write!(self.out, "
214#[derive(Debug, Clone, Serialize, Deserialize)]
215#[serde(untagged)]
216pub enum {} {{
217", type_identifier(&v.id))?;
218                for data in &v.data.fields {
219                    assert!(!data.optional);
220                    let boxed = if data.name == "definition" && data.ty.name == "BlockdevOptions" {
221                        true
222                    } else {
223                        false
224                    };
225                    let ty = if boxed {
226                        format!("Box<{}>", typename(&data.ty))
227                    } else {
228                        typename(&data.ty)
229                    };
230                    writeln!(self.out, "\t#[serde(rename = \"{}\")] {}({}),", data.name, type_identifier(&data.name), ty)?;
231                }
232                writeln!(self.out, "}}")?;
233            },
234            Spec::Enum(v) => {
235                let type_id = type_identifier(&v.id);
236                write!(self.out, "
237#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
238pub enum {} {{
239", type_id)?;
240                for item in &v.data {
241                    writeln!(self.out, "\t#[serde(rename = \"{}\")] {},", item, type_identifier(item))?;
242                }
243                writeln!(self.out, "}}")?;
244                writeln!(self.out, "
245impl ::core::str::FromStr for {} {{
246    type Err = ();
247
248    fn from_str(s: &str) -> Result<Self, Self::Err> {{
249        ::qapi_spec::Enum::from_name(s).ok_or(())
250    }}
251}}
252
253unsafe impl ::qapi_spec::Enum for {} {{
254    fn discriminant(&self) -> usize {{ *self as usize }}
255
256    const COUNT: usize = {};
257    const VARIANTS: &'static [Self] = &[
258", type_id, type_id, v.data.len())?;
259                for item in &v.data {
260                    writeln!(self.out, "{}::{},", type_id, type_identifier(item))?;
261                }
262                writeln!(self.out, "
263    ];
264    const NAMES: &'static [&'static str] = &[
265")?;
266                for item in &v.data {
267                    writeln!(self.out, "\"{}\",", item)?;
268                }
269                writeln!(self.out, "
270    ];
271}}")?;
272                self.enums.insert(v.id.clone(), v);
273            },
274            Spec::Event(v) => {
275                write!(self.out, "
276#[derive(Debug, Clone, Serialize, Deserialize{})]
277", if v.data.is_empty() { ", Default" } else { "" })?;
278                if let spec::DataOrType::Type(..) = v.data {
279                    writeln!(self.out, "#[serde(transparent)]")?;
280                    writeln!(self.out, "#[repr(transparent)]")?;
281                }
282                writeln!(self.out, "pub struct {} {{", event_identifier(&v.id))?;
283                match v.data {
284                    spec::DataOrType::Type(ref ty) => {
285                        let data = spec::Value {
286                            name: "data".into(),
287                            ty: ty.clone(),
288                            optional: false,
289                        };
290                        writeln!(self.out, "{},", valuety(&data, true, &v.id))?
291                    },
292                    spec::DataOrType::Data(ref data) => for item in &data.fields {
293                        writeln!(self.out, "{},", valuety(item, true, &v.id))?;
294                    },
295                }
296                writeln!(self.out, "}}")?;
297                writeln!(self.out, "
298impl ::qapi_spec::Event for {} {{
299    const NAME: &'static str = \"{}\";
300}}", event_identifier(&v.id), v.id)?;
301                self.events.push(v);
302            },
303            Spec::Union(v) => {
304                write!(self.out, "
305#[derive(Debug, Clone, Serialize, Deserialize)]
306#[serde(tag = \"{}\")]
307pub enum {} {{
308", if let Some(ref tag) = v.discriminator { tag } else { "type" }, type_identifier(&v.id))?;
309                for data in &v.data.fields {
310                    writeln!(self.out, "\t#[serde(rename = \"{}\")]\n\t{} {{ data: {} }},", data.name, type_identifier(&data.name), typename(&data.ty))?;
311                }
312                writeln!(self.out, "}}")?;
313            },
314            Spec::CombinedUnion(v) => {
315                self.unions.insert(v.id.clone(), v);
316            },
317            Spec::PragmaWhitelist { .. } => (),
318            Spec::PragmaExceptions { .. } => (),
319            Spec::PragmaDocRequired { .. } => (),
320        }
321
322        Ok(())
323    }
324
325    fn process_structs(&mut self) -> io::Result<()> {
326        for (id, discrim) in &self.struct_discriminators {
327            let ty = self.types.get_mut(id).ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("could not find qapi type {}", id)))?;
328            let fields = replace(&mut ty.data.fields, Vec::new());
329            ty.data.fields = fields.into_iter().filter(|base| &base.name != discrim).collect();
330        }
331
332        for v in self.types.values() {
333            let struct_id = type_identifier(&v.id);
334            write!(self.out, "
335#[derive(Debug, Clone, Serialize, Deserialize{})]{}{}
336pub struct {} {{
337", if v.is_empty() { ", Default" } else { "" }, if v.wrapper_type().is_some() { "#[repr(transparent)]" } else { "" }, feature_attrs(&v.features), struct_id)?;
338            match v.base {
339                spec::DataOrType::Data(ref data) => for base in &data.fields {
340                    writeln!(self.out, "{},", valuety(base, true, &v.id))?;
341                },
342                spec::DataOrType::Type(ref ty) => {
343                    let base = spec::Value {
344                        name: "base".into(),
345                        ty: ty.clone(),
346                        optional: false,
347                    };
348                    writeln!(self.out, "#[serde(flatten)]\n{},", valuety(&base, true, &v.id))?;
349                },
350            }
351            for item in &v.data.fields {
352                writeln!(self.out, "{},", valuety(item, true, &v.id))?;
353            }
354            writeln!(self.out, "}}")?;
355
356            let basetype = match v.data.is_empty() {
357                true => match v.base {
358                    spec::DataOrType::Type(ref ty) => Some(spec::Value::new("base", ty.clone())),
359                    spec::DataOrType::Data(ref data) => data.newtype().cloned(),
360                },
361                false => None,
362            };
363            let newtype = v.newtype();
364            let wrapper = v.wrapper_type();
365            if let Some(field) = v.newtype().or(basetype.as_ref()) {
366                let field_ty = typename(&field.ty);
367                let field_name = identifier(&field.name);
368                let into = if field.optional { ".into()" } else { "" };
369                write!(self.out, "
370impl<T: Into<{}>> From<T> for {} {{
371    fn from(val: T) -> Self {{
372        Self {{
373            {}: val.into(){},
374", field_ty, struct_id, field_name, into)?;
375                if newtype.is_none() {
376                    for field in &v.data.fields {
377                        writeln!(self.out, "{}: Default::default(),", identifier(&field.name))?;
378                    }
379                }
380                write!(self.out, "
381        }}
382    }}
383}}")?;
384                if !field.optional {
385                    write!(self.out, "
386    impl AsRef<{}> for {} {{
387        fn as_ref(&self) -> &{} {{
388            &self.{}
389        }}
390    }}", field_ty, struct_id, field_ty, field_name)?;
391                }
392            }
393            if let Some(field) = wrapper {
394                let field_ty = typename(&field.ty);
395                let field_name = identifier(&field.name);
396                write!(self.out, "
397impl ::std::ops::Deref for {} {{
398    type Target = {};
399
400    fn deref(&self) -> &Self::Target {{
401        &self.{}
402    }}
403}}", struct_id, field_ty, field_name)?;
404                write!(self.out, "
405impl {} {{
406    pub fn into_inner(self) -> {} {{
407        self.{}
408    }}
409}}", struct_id, field_ty, field_name)?;
410            }
411        }
412
413        Ok(())
414    }
415
416    fn process_unions(&mut self) -> io::Result<()> {
417        for u in self.unions.values() {
418            let discrim = u.discriminator.as_ref().map(|s| &s[..]).unwrap_or("type");
419            let type_id = type_identifier(&u.id);
420            write!(self.out, "
421#[derive(Debug, Clone, Serialize, Deserialize)]
422#[serde(tag = \"{}\")]
423pub enum {} {{
424", discrim, type_id)?;
425
426            let (create_base, base, fields, enums) = match &u.base {
427                spec::DataOrType::Data(data) if data.fields.len() > 2 => (true, Some(spec::Value {
428                    name: "base".into(),
429                    ty: spec::Type {
430                        name: format!("{}Base", type_id),
431                        is_array: false,
432                        conditional: None,
433                        features: Default::default(),
434                    },
435                    optional: false,
436                }), &data.fields, &self.enums),
437                spec::DataOrType::Data(data) => (false, data.fields.iter()
438                    .find(|f| f.name != discrim).cloned(), &data.fields, &self.enums),
439                spec::DataOrType::Type(ty) => {
440                    let base = spec::Value {
441                        name: "base".into(),
442                        ty: ty.clone(),
443                        optional: false,
444                    };
445
446                    let enums = &self.enums;
447                    let ty = self.types.get_mut(&ty.name).ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("could not find qapi type {}", ty.name)))?;
448                    for field in &ty.data.fields {
449                        if field.name == discrim {
450                            self.struct_discriminators.insert(ty.id.clone(), field.name.clone());
451                        }
452                    }
453                    (false, if ty.data.fields.len() <= 1 { None } else { Some(base) }, &ty.data.fields, enums)
454                },
455            };
456            let base_fields = fields.iter().filter(|f| f.name != discrim);
457
458            let discrim_ty = fields.iter().find(|f| f.name == discrim).map(|f| &f.ty);
459            let discrim_ty = match discrim_ty {
460                Some(ty) => ty,
461                None => panic!("missing discriminator type for {}", u.id),
462            };
463            let discrim_enum = match enums.get(&discrim_ty.name) {
464                Some(e) => e,
465                None => panic!("missing discriminator enum type {}", discrim_ty.name),
466            };
467
468            let variants: Vec<_> = u.data.fields.iter().map(|f|
469                (discrim_enum.data.iter().find(|v| f.name == v.as_ref()).expect("discriminator"), Some(f))
470            ).collect();
471            let variants: Vec<_> = variants.iter().copied().chain(
472                discrim_enum.data.iter()
473                    .filter(|v| !variants.iter().any(|(vf, _)| vf.as_ref() == v.as_ref()))
474                    .map(|v| (v, None))
475            ).collect();
476            for &(variant_name, variant) in &variants {
477                if let Some(variant) = variant {
478                    assert!(!variant.optional);
479                    assert!(!variant.ty.is_array);
480                }
481
482                write!(self.out, "\t#[serde(rename = \"{}\")]\n\t{}", variant_name, type_identifier(&variant_name))?;
483
484                let field = variant.map(|variant| spec::Value {
485                    name: variant_name.to_string().clone(),
486                    ty: variant.ty.clone(),
487                    optional: false,
488                });
489                match (&base, &field) {
490                    (Some(base), Some(field)) => {
491                        writeln!(self.out, " {{")?;
492                        writeln!(self.out, "\t\t{}{},",
493                            if base.name == "base" { "#[serde(flatten)] " } else { "" },
494                            valuety(&base, false, &u.id)
495                        )?;
496                        writeln!(self.out, "\t\t#[serde(flatten)] {},", valuety(&field, false, &u.id))?;
497                        writeln!(self.out, "\t}},")?;
498                    },
499                    (Some(field), None) | (None, Some(field)) =>
500                        writeln!(self.out, "({}),", typename(&field.ty))?,
501                    (None, None) =>
502                        writeln!(self.out, ",")?,
503                }
504            }
505            writeln!(self.out, "}}")?;
506
507            if create_base {
508                write!(self.out, "
509#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct {} {{
511", base.as_ref().unwrap().ty.name)?;
512                for field in base_fields.clone() {
513                    writeln!(self.out, "\t{},", valuety(&field, true, &u.id))?;
514                }
515                writeln!(self.out, "}}")?;
516            }
517
518            write!(self.out, "
519impl {} {{
520    pub fn {}(&self) -> {} {{
521        match *self {{
522", type_identifier(&u.id), identifier(&discrim), type_identifier(&discrim_ty.name))?;
523            for &(variant_name, _) in &variants {
524                writeln!(self.out, "
525            {}::{} {{ .. }} => {}::{},", type_identifier(&u.id), type_identifier(&variant_name), type_identifier(&discrim_ty.name), type_identifier(&variant_name))?;
526            }
527            writeln!(self.out, "
528        }}
529    }}
530}}")?;
531
532            let mut duptypes = HashSet::new();
533            let mut dups = HashSet::new();
534            for variant in &u.data.fields {
535                if duptypes.contains(&&variant.ty.name) {
536                    dups.insert(&variant.ty.name);
537                } else {
538                    duptypes.insert(&variant.ty.name);
539                }
540            }
541            for variant in &u.data.fields {
542                if dups.contains(&&variant.ty.name) {
543                    continue
544                }
545                let variant_ty = typename(&variant.ty);
546                let variant_name = type_identifier(&variant.name);
547                match &base {
548                    None => {
549                        write!(self.out, "
550impl From<{}> for {} {{
551    fn from(val: {}) -> Self {{
552        Self::{}(val)
553    }}
554}}
555", variant_ty, type_id, variant_ty, variant_name)?;
556                        let ty = self.types.get(&variant.ty.name)
557                            .map(|ty| ty.wrapper_type())
558                            .or_else(|| self.unions.get(&variant.ty.name)
559                                .map(|_e| None)
560                            )
561                            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, format!("could not find qapi type {}, needed by {}", variant.ty.name, u.id)))?;
562                        if let Some(newtype) = ty {
563                            let newtype_ty = typename(&newtype.ty);
564                            write!(self.out, "
565impl From<{}> for {} {{
566    fn from(val: {}) -> Self {{
567        Self::{}({}::from(val))
568    }}
569}}
570", newtype_ty, type_id, newtype_ty, variant_name, variant_ty)?;
571                        }
572                    },
573                    Some(base) => {
574                        let base_ty = typename(&base.ty);
575                        let boxed = type_id == base_ty;
576                        let base_into = match (base.optional, boxed) {
577                            (false, false) => "val.1",
578                            (false, true) | (true, false) => "val.1.into()",
579                            (true, true) => "Some(Box::new(val.1))",
580                        };
581                        write!(self.out, "
582impl From<({}, {})> for {} {{
583    fn from(val: ({}, {})) -> Self {{
584        Self::{} {{
585            {}: val.0,
586            {}: {},
587", variant_ty, base_ty, type_id, variant_ty, base_ty, type_identifier(&variant.name), identifier(&variant.name), identifier(&base.name), base_into)?;
588                        write!(self.out, "
589        }}
590    }}
591}}
592")?;
593                    },
594                }
595            }
596        }
597
598        Ok(())
599    }
600
601    fn process_events(&mut self) -> io::Result<()> {
602        writeln!(self.out, "
603#[derive(Debug, Clone, Serialize, Deserialize)]
604#[serde(tag = \"event\")]
605pub enum Event {{")?;
606        for event in &self.events {
607            let id = event_identifier(&event.id);
608            writeln!(self.out, "\t#[serde(rename = \"{}\")] {} {{
609        {} data: {},
610        timestamp: ::qapi_spec::Timestamp,
611    }},", event.id, id, if event.data.is_empty() { "#[serde(default)] " } else { "" }, id)?;
612        }
613        writeln!(self.out, "}}")?;
614
615        writeln!(self.out, "
616impl Event {{
617    pub fn timestamp(&self) -> ::qapi_spec::Timestamp {{
618        match *self {{")?;
619        for event in &self.events {
620            writeln!(self.out, "Event::{} {{ timestamp, .. }} => timestamp,", event_identifier(&event.id))?;
621        }
622        writeln!(self.out, "
623        }}
624    }}
625}}")?;
626        Ok(())
627    }
628}
629
630fn include<W: Write>(context: &mut Context<W>, repo: &mut QemuFileRepo, path: &str) -> io::Result<()> {
631    let include_path = repo.context().join(path);
632    if context.included.contains(&include_path) {
633        return Ok(())
634    }
635    context.included.insert(include_path);
636
637    let (mut repo, str) = repo.include(path)?;
638    for item in Parser::from_string(Parser::strip_comments(&str)) {
639        context.process(item?)?;
640    }
641
642    while !context.includes.is_empty() {
643        let includes: Vec<_> = context.includes.drain(..).collect();
644
645        for inc in includes {
646            include(context, &mut repo, &inc)?;
647        }
648    }
649
650    Ok(())
651}
652
653pub fn codegen<S: AsRef<Path>, O: AsRef<Path>>(schema_path: S, out_path: O, command_trait: String) -> io::Result<HashSet<PathBuf>> {
654    let mut repo = QemuFileRepo::new(schema_path.as_ref());
655    {
656        let mut context = Context::new(File::create(out_path)?, command_trait);
657        include(&mut context, &mut repo, "qapi-schema.json")?;
658        context.process_unions()?;
659        context.process_structs()?;
660        context.process_events()?;
661        Ok(context.included)
662    }
663}