1#![doc(html_root_url = "https://docs.rs/qapi-codegen/0.11.3")]
2
3use 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
13fn type_identifier<S: AsRef<str>>(id: S) -> String {
15 identifier(id)
16}
17
18fn 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
28fn event_identifier(id: &str) -> String {
30 id.into()
31}
32
33fn 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 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 );
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 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}