mavlink_bindgen/
lib.rs

1pub use crate::error::BindGenError;
2use std::fs::{read_dir, File};
3use std::io::BufWriter;
4use std::ops::Deref;
5use std::path::{Path, PathBuf};
6use std::process::Command;
7
8pub mod binder;
9pub mod error;
10pub mod parser;
11mod util;
12
13#[derive(Debug)]
14pub struct GeneratedBinding {
15    pub module_name: String,
16    pub mavlink_xml: PathBuf,
17    pub rust_module: PathBuf,
18}
19
20#[derive(Debug)]
21pub struct GeneratedBindings {
22    pub bindings: Vec<GeneratedBinding>,
23    pub mod_rs: PathBuf,
24}
25
26/// Generate Rust MAVLink dialect binding for dialects present in `definitions_dir` into `destination_dir`.
27///
28/// If successful returns paths of generated bindings linked to their dialect definitions files.
29pub fn generate<P1: AsRef<Path>, P2: AsRef<Path>>(
30    definitions_dir: P1,
31    destination_dir: P2,
32) -> Result<GeneratedBindings, BindGenError> {
33    _generate(definitions_dir.as_ref(), destination_dir.as_ref())
34}
35
36fn _generate(
37    definitions_dir: &Path,
38    destination_dir: &Path,
39) -> Result<GeneratedBindings, BindGenError> {
40    let mut bindings = vec![];
41
42    for entry_maybe in read_dir(definitions_dir).map_err(|source| {
43        BindGenError::CouldNotReadDefinitionsDirectory {
44            source,
45            path: definitions_dir.to_path_buf(),
46        }
47    })? {
48        let entry = entry_maybe.map_err(|source| {
49            BindGenError::CouldNotReadDirectoryEntryInDefinitionsDirectory {
50                source,
51                path: definitions_dir.to_path_buf(),
52            }
53        })?;
54
55        let definition_file = PathBuf::from(entry.file_name());
56        let module_name = util::to_module_name(&definition_file);
57
58        let definition_rs = PathBuf::from(&module_name).with_extension("rs");
59
60        let dest_path = destination_dir.join(definition_rs);
61        let mut outf = BufWriter::new(File::create(&dest_path).map_err(|source| {
62            BindGenError::CouldNotCreateRustBindingsFile {
63                source,
64                dest_path: dest_path.clone(),
65            }
66        })?);
67
68        // generate code
69        parser::generate(definitions_dir, &definition_file, &mut outf)?;
70
71        bindings.push(GeneratedBinding {
72            module_name,
73            mavlink_xml: entry.path(),
74            rust_module: dest_path,
75        });
76    }
77
78    // output mod.rs
79    {
80        let dest_path = destination_dir.join("mod.rs");
81        let mut outf = File::create(&dest_path).map_err(|source| {
82            BindGenError::CouldNotCreateRustBindingsFile {
83                source,
84                dest_path: dest_path.clone(),
85            }
86        })?;
87
88        // generate code
89        binder::generate(
90            bindings
91                .iter()
92                .map(|binding| binding.module_name.deref())
93                .collect(),
94            &mut outf,
95        );
96
97        Ok(GeneratedBindings {
98            bindings,
99            mod_rs: dest_path,
100        })
101    }
102}
103
104/// Formats generated code using `rustfmt`.
105pub fn format_generated_code(result: &GeneratedBindings) {
106    if let Err(error) = Command::new("rustfmt")
107        .args(
108            result
109                .bindings
110                .iter()
111                .map(|binding| binding.rust_module.clone()),
112        )
113        .arg(result.mod_rs.clone())
114        .status()
115    {
116        eprintln!("{error}");
117    }
118}
119
120/// Prints definitions for cargo that describe which files the generated code depends on, indicating when it has to be regenerated.
121pub fn emit_cargo_build_messages(result: &GeneratedBindings) {
122    for binding in &result.bindings {
123        // Re-run build if definition file changes
124        println!(
125            "cargo:rerun-if-changed={}",
126            binding.mavlink_xml.to_string_lossy()
127        );
128    }
129}