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
26pub 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 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 {
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 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
104pub 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
120pub fn emit_cargo_build_messages(result: &GeneratedBindings) {
122 for binding in &result.bindings {
123 println!(
125 "cargo:rerun-if-changed={}",
126 binding.mavlink_xml.to_string_lossy()
127 );
128 }
129}