Skip to main content

vpp_plugin_api_gen/
lib.rs

1//! Generate Rust code for VPP API files
2//!
3//! A small Rust-based code generator that produces Rust bindings and helper scaffolding for VPP
4//! `.api` files for implementing VPP plugins. This crate can be run from a build script (see
5//! [`Builder`]) and emits both Rust source and a JSON representation of the parsed API.
6//!
7//! # Design rationale (why a Rust crate, not an extension to `vppapigen.py`)
8//!
9//! - Insulation against upstream changes: integrating with vppapigen.py via code outside of the vpp
10//!   repository like this would need to make the assumption that the vppapigen.py internal API
11//!   doesn't change in non-backwards-compatible ways which would be unreasonable to
12//!   assume. Implementing the parser standalone avoids that issue, although it does now mean that
13//!   any extensions to the API grammer used by plugins or their imports would need to be
14//!   implemented here.
15//! - Dependency control: shipping the generator as a crate avoids introducing an extra Python
16//!   dependencies into build systems that cannot be expressed via crate dependencies.
17//! - Robustness against environment: performing a `cargo build` when set into an unrelated Python
18//!   venv could lead to spurious failures if the venv doesn't have all of the Python dependencies
19//!   needed by vppapigen.py. Implementing in Rust avoids that.
20//!
21//! # Parsing strategy: why PEG (Parsing Expression Grammar) over CFG
22//!
23//! The implementation chooses a PEG-style parser (implemented in `parser.rs`) rather than a
24//! traditional context-free grammar (CFG) parser generator for several practical reasons:
25//!
26//! - Determinism and simplicity: PEGs are deterministic and describe a single unambiguous parse for
27//!   any input (given the grammar and choice ordering).
28//!   For the `.api` format — which is relatively small, regular and unambiguous — a PEG results in
29//!   simpler grammars and parsing code without needing extra disambiguation rules.
30//! - Ergonomics in Rust: mature PEG libraries and small hand-written PEG parsers are
31//!   straightforward to implement and embed in a Rust crate. CFG tools (LR, LALR) are typically
32//!   geared toward generating parser tables and a runtime which is less ergonomic to integrate into
33//!   a small generator crate and tends to complicate error reporting and tooling.
34//! - Better error locality: PEGs (and hand-written recursive-descent parsers) make it easier to
35//!   attach localized error messages and recover cleanly for diagnostics or partial parsing.
36//!
37//! Trade-offs and caveats:
38//! - PEG grammars do not support left-recursive rules naturally. For the `.api` grammar this is not
39//!   a practical limitation because the syntax is not left-recursive and is well-suited to a PEG
40//!   style.
41//! - CFG-based parser generators can handle certain ambiguous grammars more naturally and can
42//!   produce more compact parser tables for very large and complex grammars. Here, the space of
43//!   constructs is small and well-bounded, so the simplicity and determinism of PEG were preferred.
44
45#![warn(
46    missing_docs,
47    missing_copy_implementations,
48    missing_debug_implementations
49)]
50
51use std::{
52    env,
53    fs::{DirBuilder, File},
54    io::Write,
55    path::Path,
56};
57
58use thiserror::Error;
59
60use crate::{
61    json::generate_json,
62    parser::{
63        Alias, ApiParser, CountDescriptor, Enum, Field, FieldSize, Message, Type, Union,
64        VL_API_PREFIX, VL_API_SUFFIX,
65    },
66};
67
68mod json;
69mod parser;
70
71/// Errors that can occur during API file parsing and code generation
72#[derive(Debug, Error)]
73#[non_exhaustive]
74pub enum Error {
75    /// Parser error
76    #[error("Parser error")]
77    Parser(#[from] parser::Error),
78    /// Input/output error
79    #[error("I/O error")]
80    Io(#[from] std::io::Error),
81    /// Error whilst generating JSON
82    #[error("Failed to generate JSON")]
83    Json(#[from] serde_json::Error),
84    /// Attempt to use functionality that isn't yet implemented
85    #[error("{0}")]
86    Unimplemented(String),
87}
88
89fn to_upper_camel_case(s: &str) -> String {
90    s.split('_')
91        .flat_map(|word| {
92            let word = word.to_ascii_lowercase();
93            let mut chars = word.chars();
94            let capital = chars.next().map(|x| x.to_ascii_uppercase());
95            capital.into_iter().chain(chars).collect::<Vec<_>>()
96        })
97        .collect()
98}
99
100trait ApiParserRustExt {
101    fn to_rust_type(&self, r#type: &str) -> Result<String, Error>;
102    fn to_rust_vla_elem_type(&self, r#type: &str) -> Result<String, Error>;
103}
104
105impl ApiParserRustExt for ApiParser {
106    fn to_rust_type(&self, r#type: &str) -> Result<String, Error> {
107        Ok(
108            if let Some(t) = r#type.strip_prefix(VL_API_PREFIX)
109                && let Some(t) = t.strip_suffix(VL_API_SUFFIX)
110            {
111                let global_type = self.lookup_global_type(t)?;
112                if let Some(import_module) = global_type.import_module() {
113                    format!("super::{}_api::{}", import_module, to_upper_camel_case(t))
114                } else {
115                    to_upper_camel_case(t)
116                }
117            } else {
118                r#type.to_string()
119            },
120        )
121    }
122
123    fn to_rust_vla_elem_type(&self, r#type: &str) -> Result<String, Error> {
124        Ok(match r#type {
125            // Note: u8 excluded here as it already has an alignment of 1 byte
126            "u16" | "i16" | "u32" | "i32" | "u64" | "i64" | "f64" => {
127                format!(
128                    "::vpp_plugin::vlibapi::num_unaligned::Unaligned{}",
129                    to_upper_camel_case(r#type)
130                )
131            }
132            _ => self.to_rust_type(r#type)?,
133        })
134    }
135}
136
137/// Generate Rust code for the VPP handling of APIs of from a `.api` file
138///
139/// # Examples
140///
141/// Example of use from a build script:
142///
143/// ```no_run
144/// use std::{env, path::PathBuf};
145///
146/// let output_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("src");
147/// vpp_plugin_api_gen::Builder::new("example.api", &output_dir.to_string_lossy())
148///     .expect("unable to generate API binding")
149///     .generate()
150///     .expect("unable to generate API binding");
151/// ```
152///
153/// This can then be include in the plugin as follows:
154///
155/// ```ignore
156/// mod example_api {
157///     include!(concat!(env!("OUT_DIR"), "/src/example_api.rs"));
158/// }
159/// ```
160#[derive(Debug)]
161pub struct Builder {
162    parser: ApiParser,
163
164    module: String,
165    output_file: File,
166    output_json_file: File,
167}
168
169impl Builder {
170    /// Construct a new `Builder` for the given input file and outputting to the given directory
171    ///
172    /// Both a `<api-module>_api.rs` and a `<api-module>.api.json` file will be generated in
173    /// the output directory.
174    pub fn new(input_file: &str, output_dir: &str) -> Result<Self, Error> {
175        let in_build_script = env::var("OUT_DIR").is_ok() && env::var("CARGO_MANIFEST_DIR").is_ok();
176        if in_build_script {
177            println!("cargo:cargo-rerun-if-changed={}", input_file);
178        }
179
180        // Get file name without path
181        let input_file_name = Path::new(input_file).iter().next_back().unwrap();
182        let module = Path::new(input_file_name)
183            .file_stem()
184            .unwrap()
185            .to_string_lossy()
186            .to_string();
187
188        DirBuilder::new().recursive(true).create(output_dir)?;
189        let output_file = Path::new(output_dir).join(format!("{}_api.rs", module));
190        let output_json_file = Path::new(output_dir).join(format!("{}.api.json", module));
191
192        let parser = ApiParser::new(input_file)?;
193
194        if in_build_script {
195            for import in parser.imports() {
196                println!("cargo:cargo-rerun-if-changed={}", import);
197            }
198        }
199
200        Ok(Self {
201            parser,
202            module,
203            output_file: File::create(&output_file)?,
204            output_json_file: File::create(&output_json_file)?,
205        })
206    }
207
208    /// Generate Rust code for the API file
209    ///
210    /// Both a `<api-module>_api.rs` and a `<api-module>.api.json` file will be generated in
211    /// the output directory.
212    pub fn generate(self) -> Result<(), Error> {
213        ApiGenContext {
214            parser: &self.parser,
215            module: self.module,
216            output_file: self.output_file,
217            output_json_file: self.output_json_file,
218        }
219        .generate()
220    }
221}
222
223enum EndianSwapInput<'a> {
224    Fields(&'a [Field]),
225    Alias(&'a Field),
226}
227
228/// Helper structure for API code generation
229///
230/// Mark parser as non-mutable to avoid borrow-check errors when passing references obtained from
231/// parser into `&mut self` methods.
232struct ApiGenContext<'a> {
233    parser: &'a ApiParser,
234
235    module: String,
236    output_file: File,
237    output_json_file: File,
238}
239
240impl ApiGenContext<'_> {
241    fn generate_field(&mut self, field: &Field) -> Result<(), Error> {
242        if field.r#type == "string" {
243            match &field.size {
244                Some(FieldSize::Fixed(size)) => {
245                    writeln!(
246                        self.output_file,
247                        "    pub {}: ::vpp_plugin::vlibapi::ApiFixedString<{}>,",
248                        field.name, size,
249                    )?;
250                    return Ok(());
251                }
252                Some(FieldSize::Variable(None)) => {
253                    writeln!(
254                        self.output_file,
255                        "    pub {}: ::vpp_plugin::vlibapi::ApiString,",
256                        field.name,
257                    )?;
258                    return Ok(());
259                }
260                _ => {}
261            }
262        }
263
264        match &field.size {
265            Some(FieldSize::Fixed(size)) => {
266                writeln!(
267                    self.output_file,
268                    "    pub {}: [{}; {}],",
269                    field.name,
270                    self.parser.to_rust_type(&field.r#type)?,
271                    size,
272                )?;
273            }
274            Some(FieldSize::Variable(_)) => {
275                writeln!(
276                    self.output_file,
277                    "    pub {}: [{}; 0],",
278                    field.name,
279                    self.parser.to_rust_vla_elem_type(&field.r#type)?,
280                )?;
281            }
282            None => {
283                writeln!(
284                    self.output_file,
285                    "    pub {}: {},",
286                    field.name,
287                    self.parser.to_rust_type(&field.r#type)?,
288                )?;
289            }
290        }
291        Ok(())
292    }
293
294    fn generate_debug_trait(&mut self, name: &str, fields: &[Field]) -> Result<(), Error> {
295        let upper_camel_name = to_upper_camel_case(name);
296
297        writeln!(
298            self.output_file,
299            "impl ::std::fmt::Debug for {} {{",
300            upper_camel_name
301        )?;
302        // Suppress warnings about tmp__vl_msg_id (and any similar)
303        writeln!(self.output_file, "    #[allow(non_snake_case)]")?;
304        writeln!(
305            self.output_file,
306            "    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {{"
307        )?;
308        for field in fields {
309            if !matches!(field.size, Some(FieldSize::Variable(_))) {
310                writeln!(
311                    self.output_file,
312                    "        let tmp_{} = self.{};",
313                    field.name, field.name
314                )?;
315            }
316        }
317        writeln!(
318            self.output_file,
319            "        f.debug_struct(\"{}\")",
320            upper_camel_name
321        )?;
322        for field in fields {
323            if !matches!(field.size, Some(FieldSize::Variable(_))) {
324                writeln!(
325                    self.output_file,
326                    "            .field(\"{}\", &tmp_{})",
327                    field.name, field.name
328                )?;
329            }
330        }
331        writeln!(self.output_file, "            .finish_non_exhaustive()")?;
332        writeln!(self.output_file, "    }}")?;
333        writeln!(self.output_file, "}}")?;
334        writeln!(self.output_file)?;
335
336        Ok(())
337    }
338
339    fn generate_vla_accessors(&mut self, count_field: &str, field: &Field) -> Result<(), Error> {
340        let vla_elem_type = self.parser.to_rust_vla_elem_type(&field.r#type)?;
341        writeln!(self.output_file, "    #[allow(dead_code)]")?;
342        writeln!(
343            self.output_file,
344            "    pub unsafe fn {}(&self) -> &[{}] {{",
345            field.name, vla_elem_type
346        )?;
347        writeln!(self.output_file, "        unsafe {{",)?;
348        writeln!(
349            self.output_file,
350            "            ::std::slice::from_raw_parts(std::ptr::addr_of!(self.{}).cast(), self.{} as usize)",
351            field.name, count_field
352        )?;
353        writeln!(self.output_file, "        }}",)?;
354        writeln!(self.output_file, "    }}")?;
355        writeln!(self.output_file)?;
356        writeln!(self.output_file, "    #[allow(dead_code)]")?;
357        writeln!(
358            self.output_file,
359            "    pub unsafe fn {}_mut(&mut self) -> &mut [{}] {{",
360            field.name, vla_elem_type
361        )?;
362        writeln!(self.output_file, "        unsafe {{",)?;
363        writeln!(
364            self.output_file,
365            "            std::slice::from_raw_parts_mut(std::ptr::addr_of_mut!(self.{}).cast(), self.{} as usize)",
366            field.name, count_field
367        )?;
368        writeln!(self.output_file, "        }}",)?;
369        writeln!(self.output_file, "    }}")?;
370
371        Ok(())
372    }
373
374    fn generate_message(&mut self, id: usize, message: &Message) -> Result<(), Error> {
375        let upper_camel_name = to_upper_camel_case(message.name());
376
377        if let Some(comment) = message.comment() {
378            writeln!(
379                self.output_file,
380                "#[doc = \"{}\"]",
381                comment.replace("\"", "\\\"")
382            )?;
383        }
384        let opt_derives = if message.manual_print() || message.vla_non_recursive().is_some() {
385            ""
386        } else if message.vla(self.parser).is_some() {
387            "Debug, "
388        } else {
389            "Debug, PartialEq, "
390        };
391        writeln!(self.output_file, "#[derive({}Copy, Clone)]", opt_derives)?;
392        writeln!(self.output_file, "#[repr(C, packed)]")?;
393        writeln!(self.output_file, "pub struct {} {{", upper_camel_name)?;
394        for field in message.fields() {
395            self.generate_field(field)?;
396        }
397        writeln!(self.output_file, "}}")?;
398        writeln!(self.output_file)?;
399
400        writeln!(self.output_file, "impl {} {{", upper_camel_name)?;
401        writeln!(self.output_file, "    pub const MSG_ID: u16 = {};", id)?;
402        writeln!(self.output_file)?;
403        writeln!(self.output_file, "    pub fn msg_id() -> u16 {{")?;
404        writeln!(self.output_file, "        msg_id_base() + Self::MSG_ID")?;
405        writeln!(self.output_file, "    }}")?;
406        if let Some((field, count_descr)) = message.vla(self.parser) {
407            writeln!(self.output_file)?;
408            if let Some(FieldSize::Variable(Some(count_field))) = &field.size {
409                self.generate_vla_accessors(count_field, field)?;
410                writeln!(self.output_file)?;
411            }
412            writeln!(self.output_file, "    #[allow(dead_code)]")?;
413            match count_descr {
414                CountDescriptor::Field { path, r#type } => {
415                    let var_name = path.last().cloned().unwrap_or_default();
416                    writeln!(
417                        self.output_file,
418                        "    pub fn new_message({}: {}) -> ::vpp_plugin::vlibapi::Message<Self> {{",
419                        var_name, r#type
420                    )?;
421                    // Avoid clippy::unnecessary_cast warning by only casting when the count field isn't a u32
422                    let count_expr = if r#type == "u32" {
423                        var_name.clone()
424                    } else {
425                        format!("{} as u32", var_name)
426                    };
427                    writeln!(
428                        self.output_file,
429                        "        let size = ::std::mem::size_of::<Self>() as u32 + {} * ::std::mem::size_of::<{}>() as u32;",
430                        count_expr,
431                        self.parser.to_rust_type(&field.r#type)?,
432                    )?;
433                    writeln!(
434                        self.output_file,
435                        "        let mut message = unsafe {{ ::std::mem::transmute::<::vpp_plugin::vlibapi::Message<u8>, ::vpp_plugin::vlibapi::Message<Self>>(::vpp_plugin::vlibapi::Message::new_bytes(size)) }};",
436                    )?;
437                    writeln!(
438                        self.output_file,
439                        "        message._vl_msg_id = Self::msg_id();",
440                    )?;
441                    writeln!(
442                        self.output_file,
443                        "        message.{} = {};",
444                        path.join("."),
445                        var_name,
446                    )?;
447                }
448                CountDescriptor::String(path) => {
449                    writeln!(
450                        self.output_file,
451                        "    pub fn new_message(length: u32) -> ::vpp_plugin::vlibapi::Message<Self> {{",
452                    )?;
453                    writeln!(
454                        self.output_file,
455                        "        let size = ::std::mem::size_of::<Self>() as u32 + length;",
456                    )?;
457                    writeln!(
458                        self.output_file,
459                        "        let mut message = unsafe {{ ::std::mem::transmute::<::vpp_plugin::vlibapi::Message<u8>, ::vpp_plugin::vlibapi::Message<Self>>(::vpp_plugin::vlibapi::Message::new_bytes(size)) }};",
460                    )?;
461                    writeln!(
462                        self.output_file,
463                        "        message._vl_msg_id = Self::msg_id();",
464                    )?;
465                    writeln!(
466                        self.output_file,
467                        "        unsafe {{ message.{}.set_len(length); }}",
468                        path.join("."),
469                    )?;
470                }
471            }
472            writeln!(self.output_file, "        message",)?;
473            writeln!(self.output_file, "    }}")?;
474            writeln!(self.output_file)?;
475        }
476        writeln!(self.output_file, "}}")?;
477        writeln!(self.output_file)?;
478
479        writeln!(self.output_file, "impl Default for {} {{", upper_camel_name)?;
480        writeln!(self.output_file, "    fn default() -> Self {{")?;
481        writeln!(self.output_file, "        Self {{")?;
482        for field in message.fields() {
483            if field.name == "_vl_msg_id" {
484                writeln!(self.output_file, "            _vl_msg_id: Self::msg_id(),")?;
485            } else {
486                writeln!(
487                    self.output_file,
488                    "            {}: Default::default(),",
489                    field.name,
490                )?;
491            }
492        }
493        writeln!(self.output_file, "        }}")?;
494        writeln!(self.output_file, "    }}")?;
495        writeln!(self.output_file, "}}")?;
496        writeln!(self.output_file)?;
497
498        self.generate_endian_swap(message.name(), EndianSwapInput::Fields(message.fields()))?;
499
500        // Manually implement fmt::Debug so that the zero-length (but actually variable-length)
501        // field isn't printed to avoid misleading anyone looking at the output
502        if message.vla_non_recursive().is_some() {
503            self.generate_debug_trait(message.name(), message.fields())?;
504        }
505
506        writeln!(
507            self.output_file,
508            "unsafe extern \"C\" fn {}_endian(a: *mut {}, to_net: bool) {{",
509            message.name(),
510            upper_camel_name
511        )?;
512        writeln!(self.output_file, "    unsafe {{")?;
513        writeln!(
514            self.output_file,
515            "        ::vpp_plugin::vlibapi::EndianSwap::endian_swap(&mut *a, to_net);"
516        )?;
517        writeln!(self.output_file, "    }}")?;
518        writeln!(self.output_file, "}}")?;
519        writeln!(self.output_file)?;
520
521        writeln!(
522            self.output_file,
523            "unsafe extern \"C\" fn {}_format(s: *mut u8, args: *mut ::vpp_plugin::bindings::va_list) -> *mut u8 {{",
524            message.name()
525        )?;
526        writeln!(self.output_file, "    unsafe {{")?;
527        writeln!(
528            self.output_file,
529            "        let mut args = ::std::mem::transmute::<*mut ::vpp_plugin::bindings::va_list, ::vpp_plugin::macro_support::va_list::VaList<'_>>(args);"
530        )?;
531        writeln!(
532            self.output_file,
533            "        let t = args.get::<*const {}>();",
534            upper_camel_name
535        )?;
536        writeln!(
537            self.output_file,
538            "        let mut s = ::vpp_plugin::vppinfra::vec::Vec::from_raw(s);"
539        )?;
540        writeln!(
541            self.output_file,
542            "        s.extend(format!(\"{{:?}}\", &*t).as_bytes());"
543        )?;
544        writeln!(self.output_file, "        s.into_raw()")?;
545        writeln!(self.output_file, "    }}")?;
546        writeln!(self.output_file, "}}")?;
547        writeln!(self.output_file)?;
548        if let Some((field, count_descr)) = message.vla(self.parser) {
549            write!(self.output_file, "pub ",)?;
550            writeln!(
551                self.output_file,
552                "unsafe extern \"C\" fn {}_calc_size(a: *mut {}) -> ::vpp_plugin::bindings::uword {{",
553                message.name(),
554                upper_camel_name
555            )?;
556            writeln!(self.output_file, "    unsafe {{")?;
557            write!(
558                self.output_file,
559                "        ::std::mem::size_of::<{}>() as ::vpp_plugin::bindings::uword",
560                upper_camel_name,
561            )?;
562            match count_descr {
563                CountDescriptor::Field {
564                    path: count_path,
565                    r#type: count_type,
566                } => match count_type.as_str() {
567                    "u8" => {
568                        writeln!(
569                            self.output_file,
570                            " + (*a).{} as ::vpp_plugin::bindings::uword * ::std::mem::size_of::<{}>() as ::vpp_plugin::bindings::uword",
571                            count_path.join("."),
572                            self.parser.to_rust_type(&field.r#type)?,
573                        )?;
574                    }
575                    "u16" | "u32" | "u64" | "i16" | "i32" | "i64" => {
576                        writeln!(
577                            self.output_file,
578                            " + {}::from_be((*a).{}) as ::vpp_plugin::bindings::uword * ::std::mem::size_of::<{}>() as ::vpp_plugin::bindings::uword",
579                            self.parser.to_rust_type(&count_type)?,
580                            count_path.join("."),
581                            self.parser.to_rust_type(&field.r#type)?,
582                        )?;
583                    }
584                    _ => {
585                        return Err(Error::Unimplemented(format!(
586                            "Unexpected type of variable-length array count field {} in message {}",
587                            count_path.join("."),
588                            message.name()
589                        )));
590                    }
591                },
592                CountDescriptor::String(string_path) => {
593                    writeln!(
594                        self.output_file,
595                        " + u32::from_be((*a).{}.len()) as ::vpp_plugin::bindings::uword",
596                        string_path.join("."),
597                    )?;
598                }
599            }
600            writeln!(self.output_file, "    }}")?;
601        } else {
602            writeln!(
603                self.output_file,
604                "unsafe extern \"C\" fn {}_calc_size(_a: *mut {}) -> ::vpp_plugin::bindings::uword {{",
605                message.name(),
606                upper_camel_name
607            )?;
608            writeln!(
609                self.output_file,
610                "    ::std::mem::size_of::<{}>() as ::vpp_plugin::bindings::uword",
611                upper_camel_name
612            )?;
613        }
614        writeln!(self.output_file, "}}")?;
615        writeln!(self.output_file)?;
616        Ok(())
617    }
618
619    fn generate_messages(&mut self) -> Result<(), Error> {
620        for (id, message) in self.parser.messages().iter().enumerate() {
621            self.generate_message(id, message)?;
622        }
623        Ok(())
624    }
625
626    fn generate_alias(&mut self, alias: &Alias) -> Result<(), Error> {
627        let upper_camel_name = to_upper_camel_case(&alias.field().name);
628
629        let opt_derives = if alias.manual_print() {
630            ""
631        } else {
632            "Debug, PartialEq, Default, "
633        };
634        writeln!(self.output_file, "#[derive({}Copy, Clone)]", opt_derives)?;
635        writeln!(self.output_file, "#[repr(C, packed)]")?;
636        if let Some(FieldSize::Fixed(length)) = alias.field().size {
637            writeln!(
638                self.output_file,
639                "pub struct {}(pub[{}; {}]);",
640                upper_camel_name,
641                self.parser.to_rust_type(&alias.field().r#type)?,
642                length
643            )?;
644        } else {
645            writeln!(
646                self.output_file,
647                "pub struct {}(pub {});",
648                upper_camel_name,
649                self.parser.to_rust_type(&alias.field().r#type)?
650            )?;
651        }
652        if !alias.manual_endian() {
653            self.generate_endian_swap(&alias.field().name, EndianSwapInput::Alias(alias.field()))?;
654        }
655
656        Ok(())
657    }
658
659    fn generate_aliases(&mut self) -> Result<(), Error> {
660        for message in self.parser.aliases() {
661            self.generate_alias(message)?;
662        }
663        Ok(())
664    }
665
666    fn generate_enum(&mut self, e: &Enum) -> Result<(), Error> {
667        let upper_camel_name = to_upper_camel_case(&e.name);
668
669        // Since:
670        // 1. We don't parse the memory and instead cast the message pointer due to VPP API
671        //    restrictions (no length passed to message handler); and
672        // 2. In Rust it's UB to construct an instance of an enum that doesn't match one of its
673        //    variants;
674        // then we cannot use generate an enum type here. Instead, the best that can be done to
675        // help with type safety is to use a newtype wrapper around the primitive type.
676
677        writeln!(
678            self.output_file,
679            "#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]",
680        )?;
681        writeln!(self.output_file, "#[repr(C, packed)]")?;
682        writeln!(
683            self.output_file,
684            "pub struct {}(pub {});",
685            upper_camel_name, &e.size,
686        )?;
687
688        writeln!(self.output_file)?;
689
690        for variant in &e.variants {
691            writeln!(
692                self.output_file,
693                "pub const {}: {} = {}({});",
694                variant.id, upper_camel_name, upper_camel_name, variant.value,
695            )?;
696        }
697        if !e.variants.is_empty() {
698            writeln!(self.output_file)?;
699        }
700
701        writeln!(
702            self.output_file,
703            "impl ::std::fmt::Debug for {} {{",
704            upper_camel_name
705        )?;
706        writeln!(
707            self.output_file,
708            "    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {{"
709        )?;
710        writeln!(self.output_file, "        match *self {{")?;
711        for variant in &e.variants {
712            writeln!(
713                self.output_file,
714                "            {} => f.write_str(\"{}\"),",
715                variant.id, variant.id,
716            )?;
717        }
718        writeln!(self.output_file, "            _ => {{")?;
719        writeln!(self.output_file, "                let tmp = self.0;")?;
720        writeln!(self.output_file, "                tmp.fmt(f)")?;
721        writeln!(self.output_file, "            }}")?;
722        writeln!(self.output_file, "        }}")?;
723        writeln!(self.output_file, "    }}")?;
724        writeln!(self.output_file, "}}")?;
725        writeln!(self.output_file)?;
726
727        writeln!(
728            self.output_file,
729            "impl ::vpp_plugin::vlibapi::EndianSwap for {} {{",
730            upper_camel_name
731        )?;
732        writeln!(
733            self.output_file,
734            "    unsafe fn endian_swap(&mut self, to_net: bool) {{",
735        )?;
736        // Suppress potential used variable warning
737        writeln!(self.output_file, "        let _ = to_net;",)?;
738        match e.size.as_str() {
739            "u8" => {
740                writeln!(self.output_file, "        // *self = Self(self.0) (no-op)",)?;
741            }
742            "u16" | "u32" | "u64" | "i16" | "i32" | "i64" => {
743                writeln!(self.output_file, "        *self = Self(self.0.to_be());",)?;
744            }
745            _ => {
746                return Err(Error::Unimplemented(format!(
747                    "Unexpected size type {} for enum {}",
748                    e.size, e.name
749                )));
750            }
751        }
752        writeln!(self.output_file, "    }}",)?;
753        writeln!(self.output_file, "}}")?;
754        writeln!(self.output_file)?;
755
756        Ok(())
757    }
758
759    fn generate_enumflag(&mut self, e: &Enum) -> Result<(), Error> {
760        let upper_camel_name = to_upper_camel_case(&e.name);
761
762        writeln!(
763            self.output_file,
764            "#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]",
765        )?;
766        writeln!(self.output_file, "#[repr(C, packed)]")?;
767        writeln!(
768            self.output_file,
769            "pub struct {}({});",
770            upper_camel_name, &e.size,
771        )?;
772        writeln!(self.output_file)?;
773        writeln!(self.output_file, "::vpp_plugin::bitflags::bitflags! {{",)?;
774        writeln!(
775            self.output_file,
776            "    impl {}: {} {{",
777            upper_camel_name, &e.size,
778        )?;
779
780        let upper_name = format!("{}_", e.name.to_uppercase());
781        for variant in &e.variants {
782            // For ease of use, strip off the enumflag name being used as part of each defined
783            // flag, since this is redundant as the flags are already namespaced by the enumflag
784            // type
785            let id = variant
786                .id
787                .strip_prefix(&upper_name)
788                .unwrap_or(variant.id.as_str());
789            writeln!(
790                self.output_file,
791                "        const {} = {};",
792                id, variant.value,
793            )?;
794        }
795        writeln!(self.output_file, "    }}")?;
796        writeln!(self.output_file, "}}")?;
797        writeln!(self.output_file)?;
798
799        writeln!(
800            self.output_file,
801            "impl ::std::fmt::Debug for {} {{",
802            upper_camel_name
803        )?;
804        writeln!(
805            self.output_file,
806            "    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {{"
807        )?;
808        writeln!(
809            self.output_file,
810            "        ::vpp_plugin::bitflags::parser::to_writer(self, f)"
811        )?;
812        writeln!(self.output_file, "    }}")?;
813        writeln!(self.output_file, "}}")?;
814        writeln!(self.output_file)?;
815
816        writeln!(
817            self.output_file,
818            "impl ::vpp_plugin::vlibapi::EndianSwap for {} {{",
819            upper_camel_name
820        )?;
821        writeln!(
822            self.output_file,
823            "    unsafe fn endian_swap(&mut self, to_net: bool) {{",
824        )?;
825        // Suppress potential used variable warning
826        writeln!(self.output_file, "        let _ = to_net;",)?;
827        match e.size.as_str() {
828            "u8" => {
829                writeln!(
830                    self.output_file,
831                    "        // *self = Self::from_bits_retain(self.bits()) (no-op)",
832                )?;
833            }
834            "u16" | "u32" | "u64" | "i16" | "i32" | "i64" => {
835                writeln!(
836                    self.output_file,
837                    "        *self = Self::from_bits_retain(self.bits().to_be());",
838                )?;
839            }
840            _ => {
841                return Err(Error::Unimplemented(format!(
842                    "Unexpected size type {} for enum {}",
843                    e.size, e.name
844                )));
845            }
846        }
847        writeln!(self.output_file, "    }}",)?;
848        writeln!(self.output_file, "}}")?;
849        writeln!(self.output_file)?;
850
851        Ok(())
852    }
853
854    fn generate_enums(&mut self) -> Result<(), Error> {
855        for e in self.parser.enums() {
856            self.generate_enum(e)?;
857        }
858        for e in self.parser.enumflags() {
859            self.generate_enumflag(e)?;
860        }
861        Ok(())
862    }
863
864    fn generate_union(&mut self, un: &Union) -> Result<(), Error> {
865        let upper_camel_name = to_upper_camel_case(un.name());
866
867        if let Some(comment) = un.comment() {
868            writeln!(
869                self.output_file,
870                "#[doc = \"{}\"]",
871                comment.replace("\"", "\\\"")
872            )?;
873        }
874        writeln!(self.output_file, "#[derive(Copy, Clone)]",)?;
875        writeln!(self.output_file, "#[repr(C, packed)]")?;
876        writeln!(self.output_file, "pub union {} {{", upper_camel_name)?;
877        for field in un.fields() {
878            self.generate_field(field)?;
879        }
880        writeln!(self.output_file, "}}")?;
881        writeln!(self.output_file)?;
882        // TODO: enforce manual_endian for unions?
883
884        // Note: no use of variable-length arrays is already enforced by the parser
885
886        Ok(())
887    }
888
889    fn generate_unions(&mut self) -> Result<(), Error> {
890        for un in self.parser.unions() {
891            self.generate_union(un)?;
892        }
893        Ok(())
894    }
895
896    fn generate_endian_swap(&mut self, name: &str, input: EndianSwapInput) -> Result<(), Error> {
897        writeln!(
898            self.output_file,
899            "impl ::vpp_plugin::vlibapi::EndianSwap for {} {{",
900            to_upper_camel_case(name)
901        )?;
902        writeln!(
903            self.output_file,
904            "    unsafe fn endian_swap(&mut self, to_net: bool) {{",
905        )?;
906        // Suppress potential used variable warning
907        writeln!(self.output_file, "        let _ = to_net;")?;
908        // To avoid complicating the code generation further
909        writeln!(self.output_file, "        #[allow(unused_unsafe)]")?;
910        writeln!(self.output_file, "        unsafe {{")?;
911        let fields = match input {
912            EndianSwapInput::Fields(fields) => fields,
913            EndianSwapInput::Alias(field) => std::slice::from_ref(field),
914        };
915        for field in fields {
916            let field_name = match input {
917                EndianSwapInput::Fields(_) => &field.name,
918                EndianSwapInput::Alias(_) => "0",
919            };
920
921            let mut gen_count_variable = |count_field| {
922                let count_field = fields
923                    .iter()
924                    .find(|field| &field.name == count_field)
925                    .ok_or_else(|| {
926                        Error::Unimplemented(format!(
927                            "Unable to find variable count field {}",
928                            count_field
929                        ))
930                    })?;
931                let vla_elem_type = self.parser.to_rust_vla_elem_type(&field.r#type)?;
932                writeln!(self.output_file, "            let count = if to_net {{",)?;
933                writeln!(
934                    self.output_file,
935                    "                {}::from_be(self.{})",
936                    count_field.r#type, count_field.name,
937                )?;
938                writeln!(self.output_file, "            }} else {{",)?;
939                writeln!(
940                    self.output_file,
941                    "                self.{}",
942                    count_field.name,
943                )?;
944                writeln!(self.output_file, "            }};",)?;
945                writeln!(
946                    self.output_file,
947                    "            let array = ::std::slice::from_raw_parts_mut(std::ptr::addr_of_mut!(self.{}) as *mut {}, count as usize);",
948                    field_name, vla_elem_type,
949                )?;
950                Ok::<_, Error>(())
951            };
952
953            match field.r#type.as_str() {
954                "u8" | "bool" => {
955                    writeln!(
956                        self.output_file,
957                        "            // self.{} = self.{} (no-op)",
958                        field_name, field_name
959                    )?;
960                }
961                "string" => {
962                    writeln!(
963                        self.output_file,
964                        "            ::vpp_plugin::vlibapi::EndianSwap::endian_swap(&mut self.{}, to_net);",
965                        field_name,
966                    )?;
967                }
968                "u16" | "u32" | "u64" | "i16" | "i32" | "i64" => match &field.size {
969                    Some(FieldSize::Fixed(size)) => {
970                        writeln!(self.output_file, "            for i in 0..{} {{", size)?;
971                        writeln!(
972                            self.output_file,
973                            "                self.{}[i] = self.{}[i].to_be();",
974                            field_name, field_name
975                        )?;
976                        writeln!(self.output_file, "        }}",)?;
977                    }
978                    Some(FieldSize::Variable(Some(count_field))) => {
979                        gen_count_variable(count_field)?;
980                        writeln!(self.output_file, "            for elem in array {{")?;
981                        writeln!(self.output_file, "                *elem = elem.to_be();",)?;
982                        writeln!(self.output_file, "            }}")?;
983                    }
984                    Some(FieldSize::Variable(None)) => {
985                        return Err(Error::Unimplemented(format!(
986                            "variable length array field {} without count",
987                            field_name
988                        )));
989                    }
990                    None => {
991                        writeln!(
992                            self.output_file,
993                            "            self.{} = self.{}.to_be();",
994                            field_name, field.name
995                        )?;
996                    }
997                },
998                "f64" => {
999                    writeln!(
1000                        self.output_file,
1001                        "            // self.{} = self.{} (no-op according to VPP API)",
1002                        field_name, field_name
1003                    )?;
1004                }
1005                _ => match &field.size {
1006                    Some(FieldSize::Fixed(size)) => {
1007                        writeln!(self.output_file, "            for i in 0..{} {{", size)?;
1008                        writeln!(
1009                            self.output_file,
1010                            "                ::vpp_plugin::vlibapi::EndianSwap::endian_swap(&mut self.{}[i], to_net);",
1011                            field_name
1012                        )?;
1013                        writeln!(self.output_file, "            }}",)?;
1014                    }
1015                    Some(FieldSize::Variable(Some(count_field))) => {
1016                        gen_count_variable(count_field)?;
1017                        writeln!(self.output_file, "            for elem in array {{")?;
1018                        writeln!(
1019                            self.output_file,
1020                            "                ::vpp_plugin::vlibapi::EndianSwap::endian_swap(elem, to_net);",
1021                        )?;
1022                        writeln!(self.output_file, "            }}",)?;
1023                    }
1024                    Some(FieldSize::Variable(None)) => {
1025                        return Err(Error::Unimplemented(format!(
1026                            "variable length array field {} without count",
1027                            field_name
1028                        )));
1029                    }
1030                    None => {
1031                        // Copy out the value to a temporary since the structs are packed and so it
1032                        // may not be properly aligned
1033                        writeln!(
1034                            self.output_file,
1035                            "            ::vpp_plugin::vlibapi::EndianSwap::endian_swap(&mut self.{}, to_net);",
1036                            field_name,
1037                        )?;
1038                    }
1039                },
1040            }
1041        }
1042        writeln!(self.output_file, "        }}",)?;
1043        writeln!(self.output_file, "    }}",)?;
1044        writeln!(self.output_file, "}}")?;
1045        writeln!(self.output_file)?;
1046
1047        Ok(())
1048    }
1049
1050    fn generate_type(&mut self, t: &Type) -> Result<(), Error> {
1051        let upper_camel_name = to_upper_camel_case(t.name());
1052
1053        let opt_derives = if t.manual_print() {
1054            ""
1055        } else if t.vla_non_recursive().is_some() {
1056            "Default, "
1057        } else {
1058            "Debug, PartialEq, Default, "
1059        };
1060        writeln!(self.output_file, "#[derive({}Copy, Clone)]", opt_derives)?;
1061        writeln!(self.output_file, "#[repr(C, packed)]")?;
1062        writeln!(self.output_file, "pub struct {} {{", upper_camel_name)?;
1063        for field in t.fields() {
1064            self.generate_field(field)?;
1065        }
1066        writeln!(self.output_file, "}}")?;
1067        writeln!(self.output_file)?;
1068
1069        if let Some((field, _)) = t.vla(self.parser)
1070            && let Some(FieldSize::Variable(Some(count_field))) = &field.size
1071        {
1072            writeln!(self.output_file, "impl {} {{", upper_camel_name)?;
1073            self.generate_vla_accessors(count_field, field)?;
1074            writeln!(self.output_file, "}}")?;
1075            writeln!(self.output_file)?;
1076        }
1077
1078        // Manually implement fmt::Debug so that the zero-length (but actually variable-length)
1079        // field isn't printed to avoid misleading anyone looking at the output
1080        if t.vla_non_recursive().is_some() {
1081            self.generate_debug_trait(t.name(), t.fields())?;
1082        }
1083
1084        if !t.manual_endian() {
1085            self.generate_endian_swap(t.name(), EndianSwapInput::Fields(t.fields()))?;
1086        }
1087        Ok(())
1088    }
1089
1090    fn generate_types(&mut self) -> Result<(), Error> {
1091        for t in self.parser.types() {
1092            self.generate_type(t)?;
1093        }
1094        Ok(())
1095    }
1096
1097    fn generate_register(&mut self) -> Result<(), Error> {
1098        // Avoid generating empty, unused traits/functions
1099        if self.parser.services().is_empty() {
1100            return Ok(());
1101        }
1102
1103        writeln!(self.output_file, "pub trait Handlers {{")?;
1104        for service in self.parser.services() {
1105            let caller_upper_camel = to_upper_camel_case(service.caller());
1106            let caller_message = self.parser.message(service.caller());
1107            let reply_message = if service.reply() == "null" {
1108                None
1109            } else {
1110                self.parser.message(service.reply())
1111            };
1112            let caller_message_vla = caller_message
1113                .map(|message| message.vla(self.parser).is_some())
1114                .unwrap_or(false);
1115            let reply_message_vla = reply_message
1116                .map(|message| message.vla(self.parser).is_some())
1117                .unwrap_or(false);
1118            // If the caller message is a VLA, then it's the callers of the trait have a responsibility to ensure the memory for any VLA VLA is valid, consistent with the count field.
1119            // If the reply message is a VLA, then it's the trait implementation's responsibility to ensure the memory for any VLA in the reply is valid, consistent with the count field.
1120            let unsafe_str = if caller_message_vla || reply_message_vla {
1121                "unsafe "
1122            } else {
1123                ""
1124            };
1125            if service.reply() == "null" {
1126                writeln!(
1127                    self.output_file,
1128                    "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{});",
1129                    unsafe_str,
1130                    service.caller(),
1131                    caller_upper_camel
1132                )?;
1133            } else {
1134                let reply_message = format!(
1135                    "::vpp_plugin::vlibapi::Message<{}>",
1136                    to_upper_camel_case(service.reply())
1137                );
1138                let retval_in_reply_msg = self
1139                    .parser
1140                    .message(service.reply())
1141                    .map(|reply| reply.has_retval())
1142                    .unwrap_or_default();
1143                if let Some(stream_message) = service.stream_message() {
1144                    let stream_message = format!(
1145                        "::vpp_plugin::vlibapi::Stream<{}>",
1146                        to_upper_camel_case(stream_message),
1147                    );
1148                    if retval_in_reply_msg {
1149                        writeln!(
1150                            self.output_file,
1151                            "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{}, stream: {}) -> Result<{}, i32>;",
1152                            unsafe_str,
1153                            service.caller(),
1154                            caller_upper_camel,
1155                            stream_message,
1156                            reply_message
1157                        )?;
1158                    } else {
1159                        writeln!(
1160                            self.output_file,
1161                            "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{}, stream: {}) -> {};",
1162                            unsafe_str,
1163                            service.caller(),
1164                            caller_upper_camel,
1165                            stream_message,
1166                            reply_message
1167                        )?;
1168                    }
1169                } else if service.stream() {
1170                    writeln!(
1171                        self.output_file,
1172                        "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{}, stream: ::vpp_plugin::vlibapi::Stream<{}>);",
1173                        unsafe_str,
1174                        service.caller(),
1175                        caller_upper_camel,
1176                        to_upper_camel_case(service.reply()),
1177                    )?;
1178                } else if retval_in_reply_msg {
1179                    writeln!(
1180                        self.output_file,
1181                        "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{}) -> Result<{}, i32>;",
1182                        unsafe_str,
1183                        service.caller(),
1184                        caller_upper_camel,
1185                        reply_message
1186                    )?;
1187                } else {
1188                    writeln!(
1189                        self.output_file,
1190                        "    {}fn {}(vm: &::vpp_plugin::vlib::BarrierHeldMainRef, mp: &{}) -> {};",
1191                        unsafe_str,
1192                        service.caller(),
1193                        caller_upper_camel,
1194                        reply_message
1195                    )?;
1196                }
1197            }
1198        }
1199        writeln!(self.output_file, "}}")?;
1200        writeln!(self.output_file)?;
1201
1202        for service in self.parser.services() {
1203            let caller_upper_camel = to_upper_camel_case(service.caller());
1204            writeln!(
1205                self.output_file,
1206                "unsafe extern \"C\" fn {}_handler_raw<H: Handlers>(mp: *mut {}) {{",
1207                service.caller(),
1208                caller_upper_camel
1209            )?;
1210            writeln!(self.output_file, "    unsafe {{")?;
1211            writeln!(
1212                self.output_file,
1213                "        let vm = ::vpp_plugin::vlib::BarrierHeldMainRef::from_ptr_mut("
1214            )?;
1215            writeln!(
1216                self.output_file,
1217                "            ::vpp_plugin::bindings::vlib_get_main_not_inline(),"
1218            )?;
1219            writeln!(self.output_file, "        );")?;
1220            writeln!(self.output_file, "        let mp = &*mp;")?;
1221            if service.reply() == "null" {
1222                writeln!(self.output_file, "        H::{}(vm, mp);", service.caller())?;
1223            } else {
1224                writeln!(
1225                    self.output_file,
1226                    "        ::vpp_plugin::vlibapi::registration_scope(|s| {{"
1227                )?;
1228                // TODO: check for client_index field in caller
1229                // TODO: check for context field in caller and reply
1230                writeln!(
1231                    self.output_file,
1232                    "            if let Some(reg) = s.from_client_index(vm, mp.client_index) {{"
1233                )?;
1234                let retval_in_reply_msg = self
1235                    .parser
1236                    .message(service.reply())
1237                    .map(|reply| reply.has_retval())
1238                    .unwrap_or_default();
1239                let stream_message_arg = if service.stream_message().is_some() {
1240                    ", ::vpp_plugin::vlibapi::Stream::new(reg)"
1241                } else {
1242                    ""
1243                };
1244                if service.stream() && service.stream_message().is_none() {
1245                    writeln!(
1246                        self.output_file,
1247                        "                H::{}(vm, mp, ::vpp_plugin::vlibapi::Stream::new(reg));",
1248                        service.caller()
1249                    )?;
1250                } else if retval_in_reply_msg {
1251                    writeln!(
1252                        self.output_file,
1253                        "                let mut reply = match H::{}(vm, mp{}) {{",
1254                        service.caller(),
1255                        stream_message_arg,
1256                    )?;
1257                    writeln!(self.output_file, "                    Ok(reply) => reply,")?;
1258                    writeln!(
1259                        self.output_file,
1260                        "                    Err(retval) => {} {{",
1261                        to_upper_camel_case(service.reply())
1262                    )?;
1263                    writeln!(self.output_file, "                        retval,")?;
1264                    writeln!(
1265                        self.output_file,
1266                        "                        ..Default::default()"
1267                    )?;
1268                    writeln!(self.output_file, "                    }}")?;
1269                    writeln!(self.output_file, "                    .into(),")?;
1270                    writeln!(self.output_file, "                }};")?;
1271                } else {
1272                    writeln!(
1273                        self.output_file,
1274                        "                let mut reply = H::{}(vm, mp{});",
1275                        service.caller(),
1276                        stream_message_arg,
1277                    )?;
1278                }
1279                if !service.stream() || service.stream_message().is_some() {
1280                    writeln!(
1281                        self.output_file,
1282                        "                reply.context = mp.context;",
1283                    )?;
1284                    writeln!(
1285                        self.output_file,
1286                        "                {}_endian(::std::ptr::addr_of_mut!(*reply), true);",
1287                        service.reply()
1288                    )?;
1289                    writeln!(self.output_file, "                reg.send_message(reply);")?;
1290                }
1291                writeln!(self.output_file, "            }}")?;
1292                writeln!(self.output_file, "        }})")?;
1293                writeln!(self.output_file, "    }}")?;
1294            }
1295            writeln!(self.output_file, "}}")?;
1296            writeln!(self.output_file)?;
1297        }
1298
1299        writeln!(
1300            self.output_file,
1301            "pub const MESSAGE_COUNT: u16 = {};",
1302            self.parser.messages().len()
1303        )?;
1304        writeln!(self.output_file)?;
1305
1306        writeln!(
1307            self.output_file,
1308            "static MSG_ID_BASE: ::std::sync::atomic::AtomicU16 = ::std::sync::atomic::AtomicU16::new(0);"
1309        )?;
1310        writeln!(self.output_file)?;
1311        writeln!(self.output_file, "pub fn msg_id_base() -> u16 {{")?;
1312        writeln!(
1313            self.output_file,
1314            "    MSG_ID_BASE.load(::std::sync::atomic::Ordering::Relaxed)"
1315        )?;
1316        writeln!(self.output_file, "}}")?;
1317        writeln!(self.output_file)?;
1318
1319        writeln!(
1320            self.output_file,
1321            "pub fn {}_register_messages<H: Handlers>() {{",
1322            self.module
1323        )?;
1324        writeln!(self.output_file, "    unsafe {{")?;
1325        writeln!(
1326            self.output_file,
1327            "        let am = ::vpp_plugin::bindings::vlibapi_helper_get_main();"
1328        )?;
1329        writeln!(
1330            self.output_file,
1331            "        let mut json_api_repr = ::vpp_plugin::vppinfra::vec::Vec::from_raw((*am).json_api_repr);"
1332        )?;
1333        writeln!(self.output_file, "        json_api_repr.push(",)?;
1334        writeln!(
1335            self.output_file,
1336            "            concat!(include_str!(\"{}.api.json\"), \"\\0\")",
1337            self.module
1338        )?;
1339        writeln!(self.output_file, "                .as_ptr()",)?;
1340        writeln!(self.output_file, "                .cast_mut(),",)?;
1341        writeln!(self.output_file, "        );",)?;
1342        writeln!(
1343            self.output_file,
1344            "        (*am).json_api_repr = json_api_repr.into_raw();"
1345        )?;
1346        writeln!(self.output_file)?;
1347        writeln!(
1348            self.output_file,
1349            "        let msg_id_base = ::vpp_plugin::bindings::vl_msg_api_get_msg_ids(",
1350        )?;
1351        writeln!(
1352            self.output_file,
1353            "            c\"{}_{:08x}\".as_ptr() as *mut ::std::os::raw::c_char,",
1354            self.module,
1355            self.parser.file_crc()
1356        )?;
1357        writeln!(self.output_file, "            MESSAGE_COUNT as i32,",)?;
1358        writeln!(self.output_file, "        );",)?;
1359        writeln!(self.output_file)?;
1360        for message in self.parser.messages() {
1361            writeln!(
1362                self.output_file,
1363                "        ::vpp_plugin::bindings::vl_msg_api_add_msg_name_crc("
1364            )?;
1365            writeln!(self.output_file, "            am,")?;
1366            writeln!(
1367                self.output_file,
1368                "            c\"{}_{:08x}\".as_ptr() as *mut ::std::os::raw::c_char,",
1369                message.name(),
1370                message.crc()
1371            )?;
1372            writeln!(
1373                self.output_file,
1374                "            {}::MSG_ID as u32 + msg_id_base as u32,",
1375                to_upper_camel_case(message.name()),
1376            )?;
1377            writeln!(self.output_file, "        );")?;
1378            writeln!(self.output_file)?;
1379        }
1380        for service in self.parser.services() {
1381            let caller = self.parser.message(service.caller()).unwrap();
1382            let caller_upper_camel = to_upper_camel_case(service.caller());
1383            writeln!(
1384                self.output_file,
1385                "        let mut c = vpp_plugin::bindings::vl_msg_api_msg_config_t {{"
1386            )?;
1387            writeln!(
1388                self.output_file,
1389                "            id: {}::MSG_ID as i32 + msg_id_base as i32,",
1390                caller_upper_camel,
1391            )?;
1392            writeln!(
1393                self.output_file,
1394                "            name: c\"{}\".as_ptr() as *mut ::std::os::raw::c_char,",
1395                service.caller()
1396            )?;
1397            writeln!(
1398                self.output_file,
1399                "            handler: {}_handler_raw::<H> as *mut ::std::os::raw::c_void,",
1400                service.caller()
1401            )?;
1402            writeln!(
1403                self.output_file,
1404                "            endian: {}_endian as *mut ::std::os::raw::c_void,",
1405                service.caller()
1406            )?;
1407            writeln!(
1408                self.output_file,
1409                "            format_fn: {}_format as *mut ::std::os::raw::c_void,",
1410                service.caller()
1411            )?;
1412            writeln!(
1413                self.output_file,
1414                "            tojson: std::ptr::null_mut(),"
1415            )?;
1416            writeln!(
1417                self.output_file,
1418                "            fromjson: std::ptr::null_mut(),"
1419            )?;
1420            writeln!(
1421                self.output_file,
1422                "            calc_size: {}_calc_size as *mut ::std::os::raw::c_void,",
1423                service.caller()
1424            )?;
1425            writeln!(self.output_file, "            ..Default::default()")?;
1426            writeln!(self.output_file, "        }};")?;
1427            writeln!(self.output_file, "        c.set_traced(1);")?;
1428            writeln!(self.output_file, "        c.set_replay(1);")?;
1429            // TODO: enforce always auto-endian?
1430            if caller.auto_endian() {
1431                writeln!(self.output_file, "        c.set_is_autoendian(1);")?;
1432            }
1433            writeln!(
1434                self.output_file,
1435                "        ::vpp_plugin::bindings::vl_msg_api_config(std::ptr::addr_of_mut!(c));"
1436            )?;
1437            writeln!(self.output_file)?;
1438
1439            if service.reply() != "null" {
1440                let reply = self.parser.message(service.reply()).unwrap();
1441                let reply_upper_camel = to_upper_camel_case(service.reply());
1442                writeln!(
1443                    self.output_file,
1444                    "        let mut c = vpp_plugin::bindings::vl_msg_api_msg_config_t {{"
1445                )?;
1446                writeln!(
1447                    self.output_file,
1448                    "            id: {}::MSG_ID as i32 + msg_id_base as i32,",
1449                    reply_upper_camel
1450                )?;
1451                writeln!(
1452                    self.output_file,
1453                    "            name: c\"{}\".as_ptr() as *mut ::std::os::raw::c_char,",
1454                    service.reply()
1455                )?;
1456                writeln!(
1457                    self.output_file,
1458                    "            handler: ::std::ptr::null_mut(),"
1459                )?;
1460                writeln!(
1461                    self.output_file,
1462                    "            endian: {}_endian as *mut ::std::os::raw::c_void,",
1463                    service.reply()
1464                )?;
1465                writeln!(
1466                    self.output_file,
1467                    "            format_fn: {}_format as *mut ::std::os::raw::c_void,",
1468                    service.reply()
1469                )?;
1470                writeln!(
1471                    self.output_file,
1472                    "            tojson: std::ptr::null_mut(),"
1473                )?;
1474                writeln!(
1475                    self.output_file,
1476                    "            fromjson: std::ptr::null_mut(),"
1477                )?;
1478                writeln!(
1479                    self.output_file,
1480                    "            calc_size: {}_calc_size as *mut ::std::os::raw::c_void,",
1481                    service.reply()
1482                )?;
1483                writeln!(self.output_file, "            ..Default::default()")?;
1484                writeln!(self.output_file, "        }};")?;
1485                writeln!(self.output_file, "        c.set_traced(1);")?;
1486                writeln!(self.output_file, "        c.set_replay(1);")?;
1487                // TODO: enforce always auto-endian?
1488                if reply.auto_endian() {
1489                    writeln!(self.output_file, "        c.set_is_autoendian(1);")?;
1490                }
1491                writeln!(
1492                    self.output_file,
1493                    "        ::vpp_plugin::bindings::vl_msg_api_config(std::ptr::addr_of_mut!(c));"
1494                )?;
1495                writeln!(self.output_file)?;
1496            }
1497
1498            if let Some(stream_message_name) = service.stream_message() {
1499                let stream_message = self.parser.message(stream_message_name).unwrap();
1500                let stream_message_upper_camel = to_upper_camel_case(stream_message_name);
1501                writeln!(
1502                    self.output_file,
1503                    "        let mut c = vpp_plugin::bindings::vl_msg_api_msg_config_t {{"
1504                )?;
1505                writeln!(
1506                    self.output_file,
1507                    "            id: {}::MSG_ID as i32 + msg_id_base as i32,",
1508                    stream_message_upper_camel
1509                )?;
1510                writeln!(
1511                    self.output_file,
1512                    "            name: c\"{}\".as_ptr() as *mut ::std::os::raw::c_char,",
1513                    stream_message_name
1514                )?;
1515                writeln!(
1516                    self.output_file,
1517                    "            handler: ::std::ptr::null_mut(),"
1518                )?;
1519                writeln!(
1520                    self.output_file,
1521                    "            endian: {}_endian as *mut ::std::os::raw::c_void,",
1522                    stream_message_name
1523                )?;
1524                writeln!(
1525                    self.output_file,
1526                    "            format_fn: {}_format as *mut ::std::os::raw::c_void,",
1527                    stream_message_name
1528                )?;
1529                writeln!(
1530                    self.output_file,
1531                    "            tojson: std::ptr::null_mut(),"
1532                )?;
1533                writeln!(
1534                    self.output_file,
1535                    "            fromjson: std::ptr::null_mut(),"
1536                )?;
1537                writeln!(
1538                    self.output_file,
1539                    "            calc_size: {}_calc_size as *mut ::std::os::raw::c_void,",
1540                    stream_message_name
1541                )?;
1542                writeln!(self.output_file, "            ..Default::default()")?;
1543                writeln!(self.output_file, "        }};")?;
1544                writeln!(self.output_file, "        c.set_traced(1);")?;
1545                writeln!(self.output_file, "        c.set_replay(1);")?;
1546                // TODO: enforce always auto-endian?
1547                if stream_message.auto_endian() {
1548                    writeln!(self.output_file, "        c.set_is_autoendian(1);")?;
1549                }
1550                writeln!(
1551                    self.output_file,
1552                    "        ::vpp_plugin::bindings::vl_msg_api_config(std::ptr::addr_of_mut!(c));"
1553                )?;
1554                writeln!(self.output_file)?;
1555            }
1556        }
1557
1558        writeln!(
1559            self.output_file,
1560            "        MSG_ID_BASE.store(msg_id_base, ::std::sync::atomic::Ordering::Relaxed);"
1561        )?;
1562        writeln!(self.output_file, "    }}")?;
1563        writeln!(self.output_file, "}}")?;
1564
1565        Ok(())
1566    }
1567
1568    fn generate(mut self) -> Result<(), Error> {
1569        self.output_json_file
1570            .write_all(generate_json(self.parser)?.as_bytes())?;
1571        self.generate_aliases()?;
1572        self.generate_enums()?;
1573        self.generate_unions()?;
1574        self.generate_types()?;
1575        self.generate_messages()?;
1576        self.generate_register()?;
1577        Ok(())
1578    }
1579}