1use crc_any::CRCu16;
2use std::cmp::Ordering;
3use std::collections::btree_map::Entry;
4use std::collections::{BTreeMap, HashSet};
5use std::default::Default;
6use std::fs::File;
7use std::io::{BufReader, Write};
8use std::path::{Path, PathBuf};
9use std::str::FromStr;
10
11use lazy_static::lazy_static;
12use regex::Regex;
13
14use quick_xml::{events::Event, Reader};
15
16use proc_macro2::{Ident, TokenStream};
17use quote::{format_ident, quote};
18
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22use crate::error::BindGenError;
23use crate::util;
24
25lazy_static! {
26 static ref URL_REGEX: Regex = {
27 Regex::new(concat!(
28 r"(https?://", r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", r"[a-zA-Z]{2,63}", r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*[-a-zA-Z0-9@:%_\+~#?&/=])?)" ))
34 .expect("failed to build regex")
35 };
36}
37
38#[derive(Debug, PartialEq, Clone, Default)]
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40pub struct MavProfile {
41 pub messages: BTreeMap<String, MavMessage>,
42 pub enums: BTreeMap<String, MavEnum>,
43 pub version: Option<u8>,
44 pub dialect: Option<u8>,
45}
46
47impl MavProfile {
48 fn add_message(&mut self, message: &MavMessage) {
49 match self.messages.entry(message.name.clone()) {
50 Entry::Occupied(entry) => {
51 assert!(
52 entry.get() == message,
53 "Message '{}' defined twice but definitions are different",
54 message.name
55 );
56 }
57 Entry::Vacant(entry) => {
58 entry.insert(message.clone());
59 }
60 }
61 }
62
63 fn add_enum(&mut self, enm: &MavEnum) {
64 match self.enums.entry(enm.name.clone()) {
65 Entry::Occupied(entry) => {
66 entry.into_mut().try_combine(enm);
67 }
68 Entry::Vacant(entry) => {
69 entry.insert(enm.clone());
70 }
71 }
72 }
73
74 fn update_enums(mut self) -> Self {
77 for msg in self.messages.values_mut() {
78 for field in &mut msg.fields {
79 if let Some(enum_name) = &field.enumtype {
80 if let Some(enm) = self.enums.get_mut(enum_name) {
82 if field.display == Some("bitmask".to_string()) {
84 enm.bitmask = true;
85 }
86
87 if enm.bitmask {
89 enm.primitive = Some(field.mavtype.rust_primitive_type());
90
91 for entry in &enm.entries {
93 assert!(
94 entry.value.unwrap_or_default() <= field.mavtype.max_int_value(),
95 "bitflag enum field {} of {} must be able to fit all possible values for {}",
96 field.name,
97 msg.name,
98 enum_name,
99 );
100 }
101
102 if field.display.is_none() {
104 field.display = Some("bitmask".to_string());
105 }
106 }
107 }
108 }
109 }
110 }
111 self
112 }
113
114 #[inline(always)]
126 fn emit_comments(&self, dialect_name: &str) -> TokenStream {
127 let message = format!("MAVLink {dialect_name} dialect.");
128 quote!(
129 #![doc = #message]
130 #![doc = ""]
131 #![doc = "This file was automatically generated, do not edit."]
132 )
133 }
134
135 #[inline(always)]
137 fn emit_msgs(&self) -> Vec<TokenStream> {
138 self.messages
139 .values()
140 .map(|d| d.emit_rust(self.version.is_some()))
141 .collect()
142 }
143
144 #[inline(always)]
146 fn emit_enums(&self) -> Vec<TokenStream> {
147 self.enums.values().map(|d| d.emit_rust()).collect()
148 }
149
150 #[inline(always)]
151 fn emit_deprecations(&self) -> Vec<TokenStream> {
152 self.messages
153 .values()
154 .map(|msg| {
155 msg.deprecated
156 .as_ref()
157 .map(|d| d.emit_tokens())
158 .unwrap_or_default()
159 })
160 .collect()
161 }
162
163 #[inline(always)]
165 fn emit_enum_names(&self) -> Vec<TokenStream> {
166 self.messages
167 .values()
168 .map(|msg| {
169 let name = format_ident!("{}", msg.name);
170 quote!(#name)
171 })
172 .collect()
173 }
174
175 #[inline(always)]
177 fn emit_struct_names(&self) -> Vec<TokenStream> {
178 self.messages
179 .values()
180 .map(|msg| msg.emit_struct_name())
181 .collect()
182 }
183
184 fn emit_rust(&self, dialect_name: &str) -> TokenStream {
185 let id_width = format_ident!("u32");
187
188 let comment = self.emit_comments(dialect_name);
189 let mav_minor_version = self.emit_minor_version();
190 let mav_dialect_number = self.emit_dialect_number();
191 let msgs = self.emit_msgs();
192 let deprecations = self.emit_deprecations();
193 let enum_names = self.emit_enum_names();
194 let struct_names = self.emit_struct_names();
195 let enums = self.emit_enums();
196
197 let variant_docs = self.emit_variant_description();
198
199 let mav_message =
200 self.emit_mav_message(&variant_docs, &deprecations, &enum_names, &struct_names);
201 let mav_message_all_ids = self.emit_mav_message_all_ids();
202 let mav_message_all_messages = self.emit_mav_message_all_messages();
203 let mav_message_parse = self.emit_mav_message_parse(&enum_names, &struct_names);
204 let mav_message_crc = self.emit_mav_message_crc(&id_width, &struct_names);
205 let mav_message_name = self.emit_mav_message_name(&enum_names, &struct_names);
206 let mav_message_id = self.emit_mav_message_id(&enum_names, &struct_names);
207 let mav_message_id_from_name = self.emit_mav_message_id_from_name(&struct_names);
208 let mav_message_default_from_id =
209 self.emit_mav_message_default_from_id(&enum_names, &struct_names);
210 let mav_message_random_from_id =
211 self.emit_mav_message_random_from_id(&enum_names, &struct_names);
212 let mav_message_serialize = self.emit_mav_message_serialize(&enum_names);
213 let mav_message_target_system_id = self.emit_mav_message_target_system_id();
214 let mav_message_target_component_id = self.emit_mav_message_target_component_id();
215
216 quote! {
217 #comment
218 #![allow(deprecated)]
219 #![allow(clippy::match_single_binding)]
220 #[allow(unused_imports)]
221 use num_derive::{FromPrimitive, ToPrimitive};
222 #[allow(unused_imports)]
223 use num_traits::{FromPrimitive, ToPrimitive};
224 #[allow(unused_imports)]
225 use bitflags::{bitflags, Flags};
226 #[allow(unused_imports)]
227 use mavlink_core::{MavlinkVersion, Message, MessageData, bytes::Bytes, bytes_mut::BytesMut, types::CharArray};
228
229 #[cfg(feature = "serde")]
230 use serde::{Serialize, Deserialize};
231
232 #[cfg(feature = "arbitrary")]
233 use arbitrary::Arbitrary;
234
235 #[cfg(feature = "ts")]
236 use ts_rs::TS;
237
238 #mav_minor_version
239 #mav_dialect_number
240
241 #(#enums)*
242
243 #(#msgs)*
244
245 #[derive(Clone, PartialEq, Debug)]
246 #mav_message
247
248 impl MavMessage {
249 #mav_message_all_ids
250 #mav_message_all_messages
251 }
252
253 impl Message for MavMessage {
254 #mav_message_parse
255 #mav_message_name
256 #mav_message_id
257 #mav_message_id_from_name
258 #mav_message_default_from_id
259 #mav_message_random_from_id
260 #mav_message_serialize
261 #mav_message_crc
262 #mav_message_target_system_id
263 #mav_message_target_component_id
264 }
265 }
266 }
267
268 #[inline(always)]
269 fn emit_mav_message(
270 &self,
271 docs: &[TokenStream],
272 deprecations: &[TokenStream],
273 enums: &[TokenStream],
274 structs: &[TokenStream],
275 ) -> TokenStream {
276 quote! {
277 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278 #[cfg_attr(feature = "serde", serde(tag = "type"))]
279 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
280 #[cfg_attr(feature = "ts", derive(TS))]
281 #[cfg_attr(feature = "ts", ts(export))]
282 #[repr(u32)]
283 pub enum MavMessage {
284 #(#docs #deprecations #enums(#structs),)*
285 }
286 }
287 }
288
289 fn emit_variant_description(&self) -> Vec<TokenStream> {
290 self.messages
291 .values()
292 .map(|msg| {
293 let mut ts = TokenStream::new();
294
295 if let Some(doc) = msg.description.as_ref() {
296 let doc = format!("{doc}{}", if doc.ends_with('.') { "" } else { "." });
297 let doc = URL_REGEX.replace_all(&doc, "<$1>");
298 ts.extend(quote!(#[doc = #doc]));
299
300 ts.extend(quote!(#[doc = ""]));
302 }
303
304 let id = format!("ID: {}", msg.id);
305 ts.extend(quote!(#[doc = #id]));
306
307 ts
308 })
309 .collect()
310 }
311
312 #[inline(always)]
313 fn emit_mav_message_all_ids(&self) -> TokenStream {
314 let mut message_ids = self.messages.values().map(|m| m.id).collect::<Vec<u32>>();
315 message_ids.sort();
316
317 quote!(
318 pub const fn all_ids() -> &'static [u32] {
319 &[#(#message_ids),*]
320 }
321 )
322 }
323
324 #[inline(always)]
325 fn emit_minor_version(&self) -> TokenStream {
326 if let Some(version) = self.version {
327 quote! (pub const MINOR_MAVLINK_VERSION: u8 = #version;)
328 } else {
329 TokenStream::default()
330 }
331 }
332
333 #[inline(always)]
334 fn emit_dialect_number(&self) -> TokenStream {
335 if let Some(dialect) = self.dialect {
336 quote! (pub const DIALECT_NUMBER: u8 = #dialect;)
337 } else {
338 TokenStream::default()
339 }
340 }
341
342 #[inline(always)]
343 fn emit_mav_message_parse(
344 &self,
345 enums: &[TokenStream],
346 structs: &[TokenStream],
347 ) -> TokenStream {
348 let id_width = format_ident!("u32");
349
350 quote! {
351 fn parse(version: MavlinkVersion, id: #id_width, payload: &[u8]) -> Result<Self, ::mavlink_core::error::ParserError> {
352 match id {
353 #(#structs::ID => #structs::deser(version, payload).map(Self::#enums),)*
354 _ => {
355 Err(::mavlink_core::error::ParserError::UnknownMessage { id })
356 },
357 }
358 }
359 }
360 }
361
362 #[inline(always)]
363 fn emit_mav_message_crc(&self, id_width: &Ident, structs: &[TokenStream]) -> TokenStream {
364 quote! {
365 fn extra_crc(id: #id_width) -> u8 {
366 match id {
367 #(#structs::ID => #structs::EXTRA_CRC,)*
368 _ => {
369 0
370 },
371 }
372 }
373 }
374 }
375
376 #[inline(always)]
377 fn emit_mav_message_name(&self, enums: &[TokenStream], structs: &[TokenStream]) -> TokenStream {
378 quote! {
379 fn message_name(&self) -> &'static str {
380 match self {
381 #(Self::#enums(..) => #structs::NAME,)*
382 }
383 }
384 }
385 }
386
387 #[inline(always)]
388 fn emit_mav_message_id(&self, enums: &[TokenStream], structs: &[TokenStream]) -> TokenStream {
389 let id_width = format_ident!("u32");
390 quote! {
391 fn message_id(&self) -> #id_width {
392 match self {
393 #(Self::#enums(..) => #structs::ID,)*
394 }
395 }
396 }
397 }
398
399 #[inline(always)]
400 fn emit_mav_message_id_from_name(&self, structs: &[TokenStream]) -> TokenStream {
401 quote! {
402 fn message_id_from_name(name: &str) -> Option<u32> {
403 match name {
404 #(#structs::NAME => Some(#structs::ID),)*
405 _ => {
406 None
407 }
408 }
409 }
410 }
411 }
412
413 #[inline(always)]
414 fn emit_mav_message_default_from_id(
415 &self,
416 enums: &[TokenStream],
417 structs: &[TokenStream],
418 ) -> TokenStream {
419 quote! {
420 fn default_message_from_id(id: u32) -> Option<Self> {
421 match id {
422 #(#structs::ID => Some(Self::#enums(#structs::default())),)*
423 _ => {
424 None
425 }
426 }
427 }
428 }
429 }
430
431 #[inline(always)]
432 fn emit_mav_message_random_from_id(
433 &self,
434 enums: &[TokenStream],
435 structs: &[TokenStream],
436 ) -> TokenStream {
437 quote! {
438 #[cfg(feature = "arbitrary")]
439 fn random_message_from_id<R: rand::RngCore>(id: u32, rng: &mut R) -> Option<Self> {
440 match id {
441 #(#structs::ID => Some(Self::#enums(#structs::random(rng))),)*
442 _ => None,
443 }
444 }
445 }
446 }
447
448 #[inline(always)]
449 fn emit_mav_message_serialize(&self, enums: &Vec<TokenStream>) -> TokenStream {
450 quote! {
451 fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize {
452 match self {
453 #(Self::#enums(body) => body.ser(version, bytes),)*
454 }
455 }
456 }
457 }
458
459 #[inline(always)]
460 fn emit_mav_message_target_system_id(&self) -> TokenStream {
461 let arms: Vec<TokenStream> = self
462 .messages
463 .values()
464 .filter(|msg| msg.fields.iter().any(|f| f.name == "target_system"))
465 .map(|msg| {
466 let variant = format_ident!("{}", msg.name);
467 quote!(Self::#variant(inner) => Some(inner.target_system),)
468 })
469 .collect();
470
471 quote! {
472 fn target_system_id(&self) -> Option<u8> {
473 match self {
474 #(#arms)*
475 _ => None,
476 }
477 }
478 }
479 }
480
481 #[inline(always)]
482 fn emit_mav_message_target_component_id(&self) -> TokenStream {
483 let arms: Vec<TokenStream> = self
484 .messages
485 .values()
486 .filter(|msg| msg.fields.iter().any(|f| f.name == "target_component"))
487 .map(|msg| {
488 let variant = format_ident!("{}", msg.name);
489 quote!(Self::#variant(inner) => Some(inner.target_component),)
490 })
491 .collect();
492
493 quote! {
494 fn target_component_id(&self) -> Option<u8> {
495 match self {
496 #(#arms)*
497 _ => None,
498 }
499 }
500 }
501 }
502
503 #[inline(always)]
504 fn emit_mav_message_all_messages(&self) -> TokenStream {
505 let mut entries = self
506 .messages
507 .values()
508 .map(|msg| (msg.id, msg.emit_struct_name()))
509 .collect::<Vec<_>>();
510
511 entries.sort_by_key(|(id, _)| *id);
512
513 let pairs = entries
514 .into_iter()
515 .map(|(_, struct_name)| quote!((#struct_name::NAME, #struct_name::ID)))
516 .collect::<Vec<_>>();
517
518 quote! {
519 pub const fn all_messages() -> &'static [(&'static str, u32)] {
520 &[#(#pairs),*]
521 }
522 }
523 }
524}
525
526#[derive(Debug, PartialEq, Eq, Clone, Default)]
527#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
528pub struct MavEnum {
529 pub name: String,
530 pub description: Option<String>,
531 pub entries: Vec<MavEnumEntry>,
532 pub primitive: Option<String>,
536 pub bitmask: bool,
537 pub deprecated: Option<MavDeprecation>,
538}
539
540impl MavEnum {
541 fn try_combine(&mut self, enm: &Self) {
542 if self.name == enm.name {
543 for enum_entry in &enm.entries {
544 let found_entry = self.entries.iter().find(|elem| {
545 elem.name == enum_entry.name && elem.value.unwrap() == enum_entry.value.unwrap()
546 });
547 match found_entry {
548 Some(entry) => panic!("Enum entry {} already exists", entry.name),
549 None => self.entries.push(enum_entry.clone()),
550 }
551 }
552 }
553 }
554
555 fn emit_defs(&self) -> Vec<TokenStream> {
556 let mut cnt = 0u64;
557 self.entries
558 .iter()
559 .map(|enum_entry| {
560 let name = format_ident!("{}", enum_entry.name.clone());
561 let value;
562
563 let deprecation = enum_entry.emit_deprecation();
564
565 let description = if let Some(description) = enum_entry.description.as_ref() {
566 let description = URL_REGEX.replace_all(description, "<$1>");
567 quote!(#[doc = #description])
568 } else {
569 quote!()
570 };
571
572 if enum_entry.value.is_none() {
573 cnt += 1;
574 value = quote!(#cnt);
575 } else {
576 let tmp_value = enum_entry.value.unwrap();
577 cnt = cnt.max(tmp_value);
578 let tmp = TokenStream::from_str(&tmp_value.to_string()).unwrap();
579 value = quote!(#tmp);
580 }
581 if self.primitive.is_some() {
582 quote! {
583 #deprecation
584 #description
585 const #name = #value;
586 }
587 } else {
588 quote! {
589 #deprecation
590 #description
591 #name = #value,
592 }
593 }
594 })
595 .collect()
596 }
597
598 #[inline(always)]
599 fn emit_name(&self) -> TokenStream {
600 let name = format_ident!("{}", self.name);
601 quote!(#name)
602 }
603
604 #[inline(always)]
605 fn emit_const_default(&self) -> TokenStream {
606 let default = format_ident!("{}", self.entries[0].name);
607 quote!(pub const DEFAULT: Self = Self::#default;)
608 }
609
610 #[inline(always)]
611 fn emit_deprecation(&self) -> TokenStream {
612 self.deprecated
613 .as_ref()
614 .map(|d| d.emit_tokens())
615 .unwrap_or_default()
616 }
617
618 fn emit_rust(&self) -> TokenStream {
619 let defs = self.emit_defs();
620 let enum_name = self.emit_name();
621 let const_default = self.emit_const_default();
622
623 let deprecated = self.emit_deprecation();
624
625 let description = if let Some(description) = self.description.as_ref() {
626 let desc = URL_REGEX.replace_all(description, "<$1>");
627 quote!(#[doc = #desc])
628 } else {
629 quote!()
630 };
631
632 let mav_bool_impl = if self.name == "MavBool"
633 && self
634 .entries
635 .iter()
636 .any(|entry| entry.name == "MAV_BOOL_TRUE")
637 {
638 quote!(
639 pub fn as_bool(&self) -> bool {
640 self.contains(Self::MAV_BOOL_TRUE)
641 }
642 )
643 } else {
644 quote!()
645 };
646
647 let enum_def;
648 if let Some(primitive) = self.primitive.clone() {
649 let primitive = format_ident!("{}", primitive);
650 enum_def = quote! {
651 bitflags!{
652 #[cfg_attr(feature = "ts", derive(TS))]
653 #[cfg_attr(feature = "ts", ts(export, type = "number"))]
654 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
655 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
656 #[derive(Debug, Copy, Clone, PartialEq)]
657 #deprecated
658 #description
659 pub struct #enum_name: #primitive {
660 #(#defs)*
661 }
662 }
663 };
664 } else {
665 enum_def = quote! {
666 #[cfg_attr(feature = "ts", derive(TS))]
667 #[cfg_attr(feature = "ts", ts(export))]
668 #[derive(Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
669 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
670 #[cfg_attr(feature = "serde", serde(tag = "type"))]
671 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
672 #[repr(u32)]
673 #deprecated
674 #description
675 pub enum #enum_name {
676 #(#defs)*
677 }
678 };
679 }
680
681 quote! {
682 #enum_def
683
684 impl #enum_name {
685 #const_default
686 #mav_bool_impl
687 }
688
689 impl Default for #enum_name {
690 fn default() -> Self {
691 Self::DEFAULT
692 }
693 }
694 }
695 }
696}
697
698#[derive(Debug, PartialEq, Eq, Clone, Default)]
699#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
700pub struct MavEnumEntry {
701 pub value: Option<u64>,
702 pub name: String,
703 pub description: Option<String>,
704 pub params: Option<Vec<String>>,
705 pub deprecated: Option<MavDeprecation>,
706}
707
708impl MavEnumEntry {
709 #[inline(always)]
710 fn emit_deprecation(&self) -> TokenStream {
711 self.deprecated
712 .as_ref()
713 .map(|d| d.emit_tokens())
714 .unwrap_or_default()
715 }
716}
717
718#[derive(Debug, PartialEq, Clone, Default)]
719#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
720pub struct MavMessage {
721 pub id: u32,
722 pub name: String,
723 pub description: Option<String>,
724 pub fields: Vec<MavField>,
725 pub deprecated: Option<MavDeprecation>,
726}
727
728impl MavMessage {
729 fn emit_struct_name(&self) -> TokenStream {
732 let name = format_ident!("{}", format!("{}_DATA", self.name));
733 quote!(#name)
734 }
735
736 #[inline(always)]
737 fn emit_name_types(&self) -> (Vec<TokenStream>, usize) {
738 let mut encoded_payload_len: usize = 0;
739 let field_toks = self
740 .fields
741 .iter()
742 .map(|field| {
743 let nametype = field.emit_name_type();
744 encoded_payload_len += field.mavtype.len();
745
746 let description = field.emit_description();
747
748 let serde_default = if field.is_extension {
752 if field.enumtype.is_some() {
753 quote!(#[cfg_attr(feature = "serde", serde(default))])
754 } else {
755 quote!(#[cfg_attr(feature = "serde", serde(default = "crate::RustDefault::rust_default"))])
756 }
757 } else {
758 quote!()
759 };
760
761 let serde_with_attr = if matches!(field.mavtype, MavType::Array(_, _)) {
762 quote!(
763 #[cfg_attr(feature = "serde", serde(with = "serde_arrays"))]
764 #[cfg_attr(feature = "ts", ts(type = "Array<number>"))]
765 )
766 } else if matches!(field.mavtype, MavType::CharArray(_)) {
767 quote!(
768 #[cfg_attr(feature = "ts", ts(type = "string"))]
769 )
770 } else {
771 quote!()
772 };
773
774 quote! {
775 #description
776 #serde_default
777 #serde_with_attr
778 #nametype
779 }
780 })
781 .collect::<Vec<TokenStream>>();
782 (field_toks, encoded_payload_len)
783 }
784
785 #[inline(always)]
787 fn emit_description(&self) -> TokenStream {
788 let mut ts = TokenStream::new();
789 if let Some(doc) = self.description.as_ref() {
790 let doc = format!("{doc}{}", if doc.ends_with('.') { "" } else { "." });
791 let doc = URL_REGEX.replace_all(&doc, "<$1>");
793 ts.extend(quote!(#[doc = #doc]));
794 ts.extend(quote!(#[doc = ""]));
796 }
797 let id = format!("ID: {}", self.id);
798 ts.extend(quote!(#[doc = #id]));
799 ts
800 }
801
802 #[inline(always)]
803 fn emit_serialize_vars(&self) -> TokenStream {
804 let (base_fields, ext_fields): (Vec<_>, Vec<_>) =
805 self.fields.iter().partition(|f| !f.is_extension);
806 let ser_vars = base_fields.iter().map(|f| f.rust_writer());
807 let ser_ext_vars = ext_fields.iter().map(|f| f.rust_writer());
808 quote! {
809 let mut __tmp = BytesMut::new(bytes);
810
811 #[allow(clippy::absurd_extreme_comparisons)]
819 #[allow(unused_comparisons)]
820 if __tmp.remaining() < Self::ENCODED_LEN {
821 panic!(
822 "buffer is too small (need {} bytes, but got {})",
823 Self::ENCODED_LEN,
824 __tmp.remaining(),
825 )
826 }
827
828 #(#ser_vars)*
829 if matches!(version, MavlinkVersion::V2) {
830 #(#ser_ext_vars)*
831 let len = __tmp.len();
832 ::mavlink_core::utils::remove_trailing_zeroes(&bytes[..len])
833 } else {
834 __tmp.len()
835 }
836 }
837 }
838
839 #[inline(always)]
840 fn emit_deserialize_vars(&self) -> TokenStream {
841 let deser_vars = self
842 .fields
843 .iter()
844 .map(|f| f.rust_reader())
845 .collect::<Vec<TokenStream>>();
846
847 if deser_vars.is_empty() {
848 quote! {
850 Ok(Self::default())
851 }
852 } else {
853 quote! {
854 let avail_len = __input.len();
855
856 let mut payload_buf = [0; Self::ENCODED_LEN];
857 let mut buf = if avail_len < Self::ENCODED_LEN {
858 payload_buf[0..avail_len].copy_from_slice(__input);
860 Bytes::new(&payload_buf)
861 } else {
862 Bytes::new(__input)
864 };
865
866 let mut __struct = Self::default();
867 #(#deser_vars)*
868 Ok(__struct)
869 }
870 }
871 }
872
873 #[inline(always)]
874 fn emit_default_impl(&self) -> TokenStream {
875 let msg_name = self.emit_struct_name();
876 quote! {
877 impl Default for #msg_name {
878 fn default() -> Self {
879 Self::DEFAULT.clone()
880 }
881 }
882 }
883 }
884
885 #[inline(always)]
886 fn emit_deprecation(&self) -> TokenStream {
887 self.deprecated
888 .as_ref()
889 .map(|d| d.emit_tokens())
890 .unwrap_or_default()
891 }
892
893 #[inline(always)]
894 fn emit_const_default(&self, dialect_has_version: bool) -> TokenStream {
895 let initializers = self
896 .fields
897 .iter()
898 .map(|field| field.emit_default_initializer(dialect_has_version));
899 quote!(pub const DEFAULT: Self = Self { #(#initializers)* };)
900 }
901
902 fn emit_rust(&self, dialect_has_version: bool) -> TokenStream {
903 let msg_name = self.emit_struct_name();
904 let id = self.id;
905 let name = self.name.clone();
906 let extra_crc = extra_crc(self);
907 let (name_types, payload_encoded_len) = self.emit_name_types();
908 assert!(
909 payload_encoded_len <= 255,
910 "maximum payload length is 255 bytes"
911 );
912
913 let deser_vars = self.emit_deserialize_vars();
914 let serialize_vars = self.emit_serialize_vars();
915 let const_default = self.emit_const_default(dialect_has_version);
916 let default_impl = self.emit_default_impl();
917
918 let deprecation = self.emit_deprecation();
919
920 let description = self.emit_description();
921
922 quote! {
923 #deprecation
924 #description
925 #[derive(Debug, Clone, PartialEq)]
926 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
927 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
928 #[cfg_attr(feature = "ts", derive(TS))]
929 #[cfg_attr(feature = "ts", ts(export))]
930 pub struct #msg_name {
931 #(#name_types)*
932 }
933
934 impl #msg_name {
935 pub const ENCODED_LEN: usize = #payload_encoded_len;
936 #const_default
937
938 #[cfg(feature = "arbitrary")]
939 pub fn random<R: rand::RngCore>(rng: &mut R) -> Self {
940 use arbitrary::{Unstructured, Arbitrary};
941 let mut buf = [0u8; 1024];
942 rng.fill_bytes(&mut buf);
943 let mut unstructured = Unstructured::new(&buf);
944 Self::arbitrary(&mut unstructured).unwrap_or_default()
945 }
946 }
947
948 #default_impl
949
950 impl MessageData for #msg_name {
951 type Message = MavMessage;
952
953 const ID: u32 = #id;
954 const NAME: &'static str = #name;
955 const EXTRA_CRC: u8 = #extra_crc;
956 const ENCODED_LEN: usize = #payload_encoded_len;
957
958 fn deser(_version: MavlinkVersion, __input: &[u8]) -> Result<Self, ::mavlink_core::error::ParserError> {
959 #deser_vars
960 }
961
962 fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize {
963 #serialize_vars
964 }
965 }
966 }
967 }
968
969 fn validate_unique_fields(&self) {
973 let mut seen: HashSet<&str> = HashSet::new();
974 for f in &self.fields {
975 let name: &str = &f.name;
976 assert!(
977 seen.insert(name),
978 "Duplicate field '{}' found in message '{}' while generating bindings",
979 name,
980 self.name
981 );
982 }
983 }
984
985 fn validate_field_count(&self) {
987 assert!(
988 !self.fields.is_empty(),
989 "Message '{}' does not any fields",
990 self.name
991 );
992 assert!(
993 self.fields.len() <= 64,
994 "Message '{}' has more then 64 fields",
995 self.name
996 );
997 }
998}
999
1000#[derive(Debug, PartialEq, Clone, Default)]
1001#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1002pub struct MavField {
1003 pub mavtype: MavType,
1004 pub name: String,
1005 pub description: Option<String>,
1006 pub enumtype: Option<String>,
1007 pub display: Option<String>,
1008 pub is_extension: bool,
1009}
1010
1011impl MavField {
1012 #[inline(always)]
1014 fn emit_name(&self) -> TokenStream {
1015 let name = format_ident!("{}", self.name);
1016 quote!(#name)
1017 }
1018
1019 #[inline(always)]
1021 fn emit_type(&self) -> TokenStream {
1022 let mavtype;
1023 if matches!(self.mavtype, MavType::Array(_, _)) {
1024 let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
1025 mavtype = quote!(#rt);
1026 } else if let Some(enumname) = &self.enumtype {
1027 let en = TokenStream::from_str(enumname).unwrap();
1028 mavtype = quote!(#en);
1029 } else {
1030 let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
1031 mavtype = quote!(#rt);
1032 }
1033 mavtype
1034 }
1035
1036 #[inline(always)]
1038 fn emit_description(&self) -> TokenStream {
1039 let mut ts = TokenStream::new();
1040 if let Some(val) = self.description.as_ref() {
1041 let desc = URL_REGEX.replace_all(val, "<$1>");
1042 ts.extend(quote!(#[doc = #desc]));
1043 }
1044 ts
1045 }
1046
1047 #[inline(always)]
1049 fn emit_name_type(&self) -> TokenStream {
1050 let name = self.emit_name();
1051 let fieldtype = self.emit_type();
1052 quote!(pub #name: #fieldtype,)
1053 }
1054
1055 fn rust_writer(&self) -> TokenStream {
1057 let mut name = "self.".to_string() + &self.name.clone();
1058 if self.enumtype.is_some() {
1059 if !matches!(self.mavtype, MavType::Array(_, _)) {
1062 if let Some(dsp) = &self.display {
1063 if dsp == "bitmask" {
1065 name += ".bits() as ";
1067 name += &self.mavtype.rust_type();
1068 } else {
1069 panic!("Display option not implemented");
1070 }
1071 } else {
1072 name += " as ";
1074 name += &self.mavtype.rust_type();
1075 }
1076 }
1077 }
1078 let ts = TokenStream::from_str(&name).unwrap();
1079 let name = quote!(#ts);
1080 let buf = format_ident!("__tmp");
1081 self.mavtype.rust_writer(&name, buf)
1082 }
1083
1084 fn rust_reader(&self) -> TokenStream {
1086 let _name = TokenStream::from_str(&self.name).unwrap();
1087
1088 let name = quote!(__struct.#_name);
1089 let buf = format_ident!("buf");
1090 if let Some(enum_name) = &self.enumtype {
1091 if let MavType::Array(_t, _size) = &self.mavtype {
1094 return self.mavtype.rust_reader(&name, buf);
1095 }
1096 if let Some(dsp) = &self.display {
1097 if dsp == "bitmask" {
1098 let tmp = self.mavtype.rust_reader("e!(let tmp), buf);
1100 let enum_name_ident = format_ident!("{}", enum_name);
1101 quote! {
1102 #tmp
1103 #name = #enum_name_ident::from_bits(tmp as <#enum_name_ident as Flags>::Bits)
1104 .ok_or(::mavlink_core::error::ParserError::InvalidFlag { flag_type: #enum_name, value: tmp as u64 })?;
1105 }
1106 } else {
1107 panic!("Display option not implemented");
1108 }
1109 } else {
1110 let tmp = self.mavtype.rust_reader("e!(let tmp), buf);
1112 let val = format_ident!("from_{}", &self.mavtype.rust_type());
1113 quote!(
1114 #tmp
1115 #name = FromPrimitive::#val(tmp)
1116 .ok_or(::mavlink_core::error::ParserError::InvalidEnum { enum_type: #enum_name, value: tmp as u64 })?;
1117 )
1118 }
1119 } else {
1120 self.mavtype.rust_reader(&name, buf)
1121 }
1122 }
1123
1124 #[inline(always)]
1125 fn emit_default_initializer(&self, dialect_has_version: bool) -> TokenStream {
1126 let field = self.emit_name();
1127 if matches!(self.mavtype, MavType::Array(_, _)) {
1129 let default_value = self.mavtype.emit_default_value(dialect_has_version);
1130 quote!(#field: #default_value,)
1131 } else if let Some(enumname) = &self.enumtype {
1132 let ty = TokenStream::from_str(enumname).unwrap();
1133 quote!(#field: #ty::DEFAULT,)
1134 } else {
1135 let default_value = self.mavtype.emit_default_value(dialect_has_version);
1136 quote!(#field: #default_value,)
1137 }
1138 }
1139}
1140
1141#[derive(Debug, PartialEq, Clone, Default)]
1142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1143pub enum MavType {
1144 UInt8MavlinkVersion,
1145 #[default]
1146 UInt8,
1147 UInt16,
1148 UInt32,
1149 UInt64,
1150 Int8,
1151 Int16,
1152 Int32,
1153 Int64,
1154 Char,
1155 Float,
1156 Double,
1157 CharArray(usize),
1158 Array(Box<MavType>, usize),
1159}
1160
1161impl MavType {
1162 fn parse_type(s: &str) -> Option<Self> {
1163 use self::MavType::*;
1164 match s {
1165 "uint8_t_mavlink_version" => Some(UInt8MavlinkVersion),
1166 "uint8_t" => Some(UInt8),
1167 "uint16_t" => Some(UInt16),
1168 "uint32_t" => Some(UInt32),
1169 "uint64_t" => Some(UInt64),
1170 "int8_t" => Some(Int8),
1171 "int16_t" => Some(Int16),
1172 "int32_t" => Some(Int32),
1173 "int64_t" => Some(Int64),
1174 "char" => Some(Char),
1175 "float" => Some(Float),
1176 "Double" => Some(Double),
1177 "double" => Some(Double),
1178 _ if s.starts_with("char[") => {
1179 let start = 4;
1180 let size = s[start + 1..(s.len() - 1)].parse::<usize>().ok()?;
1181 Some(CharArray(size))
1182 }
1183 _ if s.ends_with(']') => {
1184 let start = s.find('[')?;
1185 let size = s[start + 1..(s.len() - 1)].parse::<usize>().ok()?;
1186 let mtype = Self::parse_type(&s[0..start])?;
1187 Some(Array(Box::new(mtype), size))
1188 }
1189 _ => None,
1190 }
1191 }
1192
1193 pub fn rust_reader(&self, val: &TokenStream, buf: Ident) -> TokenStream {
1195 use self::MavType::*;
1196 match self {
1197 Char => quote! {#val = #buf.get_u8()?;},
1198 UInt8 => quote! {#val = #buf.get_u8()?;},
1199 UInt16 => quote! {#val = #buf.get_u16_le()?;},
1200 UInt32 => quote! {#val = #buf.get_u32_le()?;},
1201 UInt64 => quote! {#val = #buf.get_u64_le()?;},
1202 UInt8MavlinkVersion => quote! {#val = #buf.get_u8()?;},
1203 Int8 => quote! {#val = #buf.get_i8()?;},
1204 Int16 => quote! {#val = #buf.get_i16_le()?;},
1205 Int32 => quote! {#val = #buf.get_i32_le()?;},
1206 Int64 => quote! {#val = #buf.get_i64_le()?;},
1207 Float => quote! {#val = #buf.get_f32_le()?;},
1208 Double => quote! {#val = #buf.get_f64_le()?;},
1209 CharArray(size) => {
1210 quote! {
1211 let mut tmp = [0_u8; #size];
1212 for v in &mut tmp {
1213 *v = #buf.get_u8()?;
1214 }
1215 #val = CharArray::new(tmp);
1216 }
1217 }
1218 Array(t, _) => {
1219 let r = t.rust_reader("e!(let val), buf);
1220 quote! {
1221 for v in &mut #val {
1222 #r
1223 *v = val;
1224 }
1225 }
1226 }
1227 }
1228 }
1229
1230 pub fn rust_writer(&self, val: &TokenStream, buf: Ident) -> TokenStream {
1232 use self::MavType::*;
1233 match self {
1234 UInt8MavlinkVersion => quote! {#buf.put_u8(#val);},
1235 UInt8 => quote! {#buf.put_u8(#val);},
1236 Char => quote! {#buf.put_u8(#val);},
1237 UInt16 => quote! {#buf.put_u16_le(#val);},
1238 UInt32 => quote! {#buf.put_u32_le(#val);},
1239 Int8 => quote! {#buf.put_i8(#val);},
1240 Int16 => quote! {#buf.put_i16_le(#val);},
1241 Int32 => quote! {#buf.put_i32_le(#val);},
1242 Float => quote! {#buf.put_f32_le(#val);},
1243 UInt64 => quote! {#buf.put_u64_le(#val);},
1244 Int64 => quote! {#buf.put_i64_le(#val);},
1245 Double => quote! {#buf.put_f64_le(#val);},
1246 CharArray(_) => {
1247 let w = Char.rust_writer("e!(*val), buf);
1248 quote! {
1249 for val in &#val {
1250 #w
1251 }
1252 }
1253 }
1254 Array(t, _size) => {
1255 let w = t.rust_writer("e!(*val), buf);
1256 quote! {
1257 for val in &#val {
1258 #w
1259 }
1260 }
1261 }
1262 }
1263 }
1264
1265 fn len(&self) -> usize {
1267 use self::MavType::*;
1268 match self {
1269 UInt8MavlinkVersion | UInt8 | Int8 | Char => 1,
1270 UInt16 | Int16 => 2,
1271 UInt32 | Int32 | Float => 4,
1272 UInt64 | Int64 | Double => 8,
1273 CharArray(size) => *size,
1274 Array(t, size) => t.len() * size,
1275 }
1276 }
1277
1278 fn max_int_value(&self) -> u64 {
1279 match self {
1280 MavType::UInt8MavlinkVersion | MavType::UInt8 => u8::MAX as u64,
1281 MavType::UInt16 => u16::MAX as u64,
1282 MavType::UInt32 => u32::MAX as u64,
1283 MavType::UInt64 => u64::MAX,
1284 MavType::Int8 | MavType::Char | MavType::CharArray(_) => i8::MAX as u64,
1285 MavType::Int16 => i16::MAX as u64,
1286 MavType::Int32 => i32::MAX as u64,
1287 MavType::Int64 => i64::MAX as u64,
1288 MavType::Float => (1 << f32::MANTISSA_DIGITS) - 1,
1290 MavType::Double => (1 << f64::MANTISSA_DIGITS) - 1,
1291 MavType::Array(mav_type, _) => mav_type.max_int_value(),
1292 }
1293 }
1294
1295 fn order_len(&self) -> usize {
1297 use self::MavType::*;
1298 match self {
1299 UInt8MavlinkVersion | UInt8 | Int8 | Char | CharArray(_) => 1,
1300 UInt16 | Int16 => 2,
1301 UInt32 | Int32 | Float => 4,
1302 UInt64 | Int64 | Double => 8,
1303 Array(t, _) => t.len(),
1304 }
1305 }
1306
1307 pub fn primitive_type(&self) -> String {
1309 use self::MavType::*;
1310 match self {
1311 UInt8MavlinkVersion => "uint8_t".into(),
1312 UInt8 => "uint8_t".into(),
1313 Int8 => "int8_t".into(),
1314 Char => "char".into(),
1315 UInt16 => "uint16_t".into(),
1316 Int16 => "int16_t".into(),
1317 UInt32 => "uint32_t".into(),
1318 Int32 => "int32_t".into(),
1319 Float => "float".into(),
1320 UInt64 => "uint64_t".into(),
1321 Int64 => "int64_t".into(),
1322 Double => "double".into(),
1323 CharArray(_) => "char".into(),
1324 Array(t, _) => t.primitive_type(),
1325 }
1326 }
1327
1328 pub fn rust_type(&self) -> String {
1331 use self::MavType::*;
1332 match self {
1333 UInt8 | UInt8MavlinkVersion => "u8".into(),
1334 Int8 => "i8".into(),
1335 Char => "u8".into(),
1336 UInt16 => "u16".into(),
1337 Int16 => "i16".into(),
1338 UInt32 => "u32".into(),
1339 Int32 => "i32".into(),
1340 Float => "f32".into(),
1341 UInt64 => "u64".into(),
1342 Int64 => "i64".into(),
1343 Double => "f64".into(),
1344 CharArray(size) => format!("CharArray<{}>", size),
1345 Array(t, size) => format!("[{};{}]", t.rust_type(), size),
1346 }
1347 }
1348
1349 pub fn emit_default_value(&self, dialect_has_version: bool) -> TokenStream {
1350 use self::MavType::*;
1351 match self {
1352 UInt8 => quote!(0_u8),
1353 UInt8MavlinkVersion => {
1354 if dialect_has_version {
1355 quote!(MINOR_MAVLINK_VERSION)
1356 } else {
1357 quote!(0_u8)
1358 }
1359 }
1360 Int8 => quote!(0_i8),
1361 Char => quote!(0_u8),
1362 UInt16 => quote!(0_u16),
1363 Int16 => quote!(0_i16),
1364 UInt32 => quote!(0_u32),
1365 Int32 => quote!(0_i32),
1366 Float => quote!(0.0_f32),
1367 UInt64 => quote!(0_u64),
1368 Int64 => quote!(0_i64),
1369 Double => quote!(0.0_f64),
1370 CharArray(size) => quote!(CharArray::new([0_u8; #size])),
1371 Array(ty, size) => {
1372 let default_value = ty.emit_default_value(dialect_has_version);
1373 quote!([#default_value; #size])
1374 }
1375 }
1376 }
1377
1378 pub fn rust_primitive_type(&self) -> String {
1382 use self::MavType::*;
1383 match self {
1384 Array(t, _) => t.rust_primitive_type(),
1385 _ => self.rust_type(),
1386 }
1387 }
1388
1389 pub fn compare(&self, other: &Self) -> Ordering {
1391 let len = self.order_len();
1392 (-(len as isize)).cmp(&(-(other.order_len() as isize)))
1393 }
1394}
1395
1396#[derive(Debug, PartialEq, Eq, Clone, Default)]
1397#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1398pub struct MavDeprecation {
1399 pub since: String,
1401 pub replaced_by: String,
1403 pub note: Option<String>,
1404}
1405
1406impl MavDeprecation {
1407 pub fn emit_tokens(&self) -> TokenStream {
1408 let since = &self.since;
1409 let note = match &self.note {
1410 Some(str) if str.is_empty() || str.ends_with(".") => str.clone(),
1411 Some(str) => format!("{str}."),
1412 None => String::new(),
1413 };
1414 let replaced_by = if self.replaced_by.starts_with("`") {
1415 format!("See {}", self.replaced_by)
1416 } else if self.replaced_by.is_empty() {
1417 String::new()
1418 } else {
1419 format!("See `{}`", self.replaced_by)
1420 };
1421 let message = format!("{note} {replaced_by} (Deprecated since {since})");
1422 quote!(#[deprecated = #message])
1423 }
1424}
1425
1426#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1427#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1428#[cfg_attr(feature = "serde", serde(tag = "type"))]
1429pub enum MavXmlElement {
1430 Version,
1431 Mavlink,
1432 Dialect,
1433 Include,
1434 Enums,
1435 Enum,
1436 Entry,
1437 Description,
1438 Param,
1439 Messages,
1440 Message,
1441 Field,
1442 Deprecated,
1443 Wip,
1444 Extensions,
1445}
1446
1447const fn identify_element(s: &[u8]) -> Option<MavXmlElement> {
1448 use self::MavXmlElement::*;
1449 match s {
1450 b"version" => Some(Version),
1451 b"mavlink" => Some(Mavlink),
1452 b"dialect" => Some(Dialect),
1453 b"include" => Some(Include),
1454 b"enums" => Some(Enums),
1455 b"enum" => Some(Enum),
1456 b"entry" => Some(Entry),
1457 b"description" => Some(Description),
1458 b"param" => Some(Param),
1459 b"messages" => Some(Messages),
1460 b"message" => Some(Message),
1461 b"field" => Some(Field),
1462 b"deprecated" => Some(Deprecated),
1463 b"wip" => Some(Wip),
1464 b"extensions" => Some(Extensions),
1465 _ => None,
1466 }
1467}
1468
1469fn is_valid_parent(p: Option<MavXmlElement>, s: MavXmlElement) -> bool {
1470 use self::MavXmlElement::*;
1471 match s {
1472 Version => p == Some(Mavlink),
1473 Mavlink => p.is_none(),
1474 Dialect => p == Some(Mavlink),
1475 Include => p == Some(Mavlink),
1476 Enums => p == Some(Mavlink),
1477 Enum => p == Some(Enums),
1478 Entry => p == Some(Enum),
1479 Description => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1480 Param => p == Some(Entry),
1481 Messages => p == Some(Mavlink),
1482 Message => p == Some(Messages),
1483 Field => p == Some(Message),
1484 Deprecated => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1485 Wip => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1486 Extensions => p == Some(Message),
1487 }
1488}
1489
1490pub fn parse_profile(
1491 definitions_dir: &Path,
1492 definition_file: &Path,
1493 parsed_files: &mut HashSet<PathBuf>,
1494) -> Result<MavProfile, BindGenError> {
1495 let in_path = Path::new(&definitions_dir).join(definition_file);
1496 parsed_files.insert(in_path.clone()); let mut stack: Vec<MavXmlElement> = vec![];
1499
1500 let mut profile = MavProfile::default();
1501 let mut field = MavField::default();
1502 let mut message = MavMessage::default();
1503 let mut mavenum = MavEnum::default();
1504 let mut entry = MavEnumEntry::default();
1505 let mut include = PathBuf::new();
1506 let mut paramid: Option<usize> = None;
1507 let mut deprecated: Option<MavDeprecation> = None;
1508
1509 let mut xml_filter = MavXmlFilter::default();
1510 let mut events: Vec<Result<Event, quick_xml::Error>> = Vec::new();
1511 let file = File::open(&in_path).map_err(|e| BindGenError::CouldNotReadDefinitionFile {
1512 source: e,
1513 path: in_path.clone(),
1514 })?;
1515 let mut reader = Reader::from_reader(BufReader::new(file));
1516 reader.config_mut().trim_text(true);
1517
1518 let mut buf = Vec::new();
1519 loop {
1520 match reader.read_event_into(&mut buf) {
1521 Ok(Event::Eof) => {
1522 events.push(Ok(Event::Eof));
1523 break;
1524 }
1525 Ok(event) => events.push(Ok(event.into_owned())),
1526 Err(why) => events.push(Err(why)),
1527 }
1528 buf.clear();
1529 }
1530 xml_filter.filter(&mut events);
1531 let mut is_in_extension = false;
1532 for e in events {
1533 match e {
1534 Ok(Event::Start(bytes)) => {
1535 let Some(id) = identify_element(bytes.name().into_inner()) else {
1536 panic!(
1537 "unexpected element {:?}",
1538 String::from_utf8_lossy(bytes.name().into_inner())
1539 );
1540 };
1541
1542 assert!(
1543 is_valid_parent(stack.last().copied(), id),
1544 "not valid parent {:?} of {id:?}",
1545 stack.last(),
1546 );
1547
1548 match id {
1549 MavXmlElement::Extensions => {
1550 is_in_extension = true;
1551 }
1552 MavXmlElement::Message => {
1553 message = MavMessage::default();
1554 }
1555 MavXmlElement::Field => {
1556 field = MavField {
1557 is_extension: is_in_extension,
1558 ..Default::default()
1559 };
1560 }
1561 MavXmlElement::Enum => {
1562 mavenum = MavEnum::default();
1563 }
1564 MavXmlElement::Entry => {
1565 if mavenum.entries.is_empty() {
1566 mavenum.deprecated = deprecated;
1567 }
1568 deprecated = None;
1569 entry = MavEnumEntry::default();
1570 }
1571 MavXmlElement::Include => {
1572 include = PathBuf::default();
1573 }
1574 MavXmlElement::Param => {
1575 paramid = None;
1576 }
1577 MavXmlElement::Deprecated => {
1578 deprecated = Some(MavDeprecation {
1579 replaced_by: String::new(),
1580 since: String::new(),
1581 note: None,
1582 });
1583 }
1584 _ => (),
1585 }
1586 stack.push(id);
1587
1588 for attr in bytes.attributes() {
1589 let attr = attr.unwrap();
1590 match stack.last() {
1591 Some(&MavXmlElement::Enum) => {
1592 if attr.key.into_inner() == b"name" {
1593 mavenum.name = to_pascal_case(attr.value);
1594 } else if attr.key.into_inner() == b"bitmask" {
1596 mavenum.bitmask = true;
1597 }
1598 }
1599 Some(&MavXmlElement::Entry) => {
1600 match attr.key.into_inner() {
1601 b"name" => {
1602 entry.name = String::from_utf8_lossy(&attr.value).to_string();
1603 }
1604 b"value" => {
1605 let value = String::from_utf8_lossy(&attr.value);
1606 let (src, radix) = value
1608 .strip_prefix("0x")
1609 .map(|value| (value, 16))
1610 .unwrap_or((value.as_ref(), 10));
1611 entry.value = u64::from_str_radix(src, radix).ok();
1612 }
1613 _ => (),
1614 }
1615 }
1616 Some(&MavXmlElement::Message) => {
1617 match attr.key.into_inner() {
1618 b"name" => {
1619 message.name = String::from_utf8_lossy(&attr.value).to_string();
1633 }
1634 b"id" => {
1635 message.id =
1636 String::from_utf8_lossy(&attr.value).parse().unwrap();
1637 }
1638 _ => (),
1639 }
1640 }
1641 Some(&MavXmlElement::Field) => {
1642 match attr.key.into_inner() {
1643 b"name" => {
1644 let name = String::from_utf8_lossy(&attr.value);
1645 field.name = if name == "type" {
1646 "mavtype".to_string()
1647 } else {
1648 name.to_string()
1649 };
1650 }
1651 b"type" => {
1652 let r#type = String::from_utf8_lossy(&attr.value);
1653 field.mavtype = MavType::parse_type(&r#type).unwrap();
1654 }
1655 b"enum" => {
1656 field.enumtype = Some(to_pascal_case(&attr.value));
1657
1658 if let Some(e) =
1660 profile.enums.get(field.enumtype.as_ref().unwrap())
1661 {
1662 if e.bitmask {
1663 field.display = Some("bitmask".to_string());
1664 }
1665 }
1666 }
1667 b"display" => {
1668 field.display =
1669 Some(String::from_utf8_lossy(&attr.value).to_string());
1670 }
1671 _ => (),
1672 }
1673 }
1674 Some(&MavXmlElement::Param) => {
1675 if entry.params.is_none() {
1676 entry.params = Some(vec![]);
1677 }
1678 if attr.key.into_inner() == b"index" {
1679 paramid =
1680 Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1681 }
1682 }
1683 Some(&MavXmlElement::Deprecated) => match attr.key.into_inner() {
1684 b"since" => {
1685 deprecated.as_mut().unwrap().since =
1686 String::from_utf8_lossy(&attr.value).to_string();
1687 }
1688 b"replaced_by" => {
1689 deprecated.as_mut().unwrap().replaced_by =
1690 String::from_utf8_lossy(&attr.value).to_string();
1691 }
1692 _ => (),
1693 },
1694 _ => (),
1695 }
1696 }
1697 }
1698 Ok(Event::Empty(bytes)) => match bytes.name().into_inner() {
1699 b"extensions" => {
1700 is_in_extension = true;
1701 }
1702 b"entry" => {
1703 if mavenum.entries.is_empty() {
1704 mavenum.deprecated = deprecated;
1705 }
1706 deprecated = None;
1707 entry = MavEnumEntry::default();
1708 for attr in bytes.attributes() {
1709 let attr = attr.unwrap();
1710 match attr.key.into_inner() {
1711 b"name" => {
1712 entry.name = String::from_utf8_lossy(&attr.value).to_string();
1713 }
1714 b"value" => {
1715 entry.value =
1716 Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1717 }
1718 _ => (),
1719 }
1720 }
1721 mavenum.entries.push(entry.clone());
1722 }
1723 b"deprecated" => {
1724 deprecated = Some(MavDeprecation {
1725 since: String::new(),
1726 replaced_by: String::new(),
1727 note: None,
1728 });
1729 for attr in bytes.attributes() {
1730 let attr = attr.unwrap();
1731 match attr.key.into_inner() {
1732 b"since" => {
1733 deprecated.as_mut().unwrap().since =
1734 String::from_utf8_lossy(&attr.value).to_string();
1735 }
1736 b"replaced_by" => {
1737 deprecated.as_mut().unwrap().replaced_by =
1738 String::from_utf8_lossy(&attr.value).to_string();
1739 }
1740 _ => (),
1741 }
1742 }
1743 }
1744 b"field" => {
1745 let mut field = MavField {
1746 is_extension: is_in_extension,
1747 ..Default::default()
1748 };
1749 for attr in bytes.attributes() {
1750 let attr = attr.unwrap();
1751 match attr.key.into_inner() {
1752 b"name" => {
1753 let name = String::from_utf8_lossy(&attr.value);
1754 field.name = if name == "type" {
1755 "mavtype".to_string()
1756 } else {
1757 name.to_string()
1758 };
1759 }
1760 b"type" => {
1761 let r#type = String::from_utf8_lossy(&attr.value);
1762 field.mavtype = MavType::parse_type(&r#type).unwrap();
1763 }
1764 b"enum" => {
1765 field.enumtype = Some(to_pascal_case(&attr.value));
1766
1767 if let Some(e) = profile.enums.get(field.enumtype.as_ref().unwrap())
1769 {
1770 if e.bitmask {
1771 field.display = Some("bitmask".to_string());
1772 }
1773 }
1774 }
1775 b"display" => {
1776 field.display =
1777 Some(String::from_utf8_lossy(&attr.value).to_string());
1778 }
1779 _ => (),
1780 }
1781 }
1782 message.fields.push(field);
1783 }
1784 _ => (),
1785 },
1786 Ok(Event::Text(bytes)) => {
1787 let s = String::from_utf8_lossy(&bytes).to_string();
1788
1789 use self::MavXmlElement::*;
1790 match (stack.last(), stack.get(stack.len() - 2)) {
1791 (Some(&Description), Some(&Message)) => {
1792 message.description = Some(s.replace('\n', " "));
1793 }
1794 (Some(&Field), Some(&Message)) => {
1795 field.description = Some(s.replace('\n', " "));
1796 }
1797 (Some(&Description), Some(&Enum)) => {
1798 mavenum.description = Some(s.replace('\n', " "));
1799 }
1800 (Some(&Description), Some(&Entry)) => {
1801 entry.description = Some(s.replace('\n', " "));
1802 }
1803 (Some(&Param), Some(&Entry)) => {
1804 if let Some(params) = entry.params.as_mut() {
1805 let paramid = paramid.unwrap();
1808 if params.len() < paramid {
1809 for index in params.len()..paramid {
1810 params.insert(index, String::from("The use of this parameter (if any), must be defined in the requested message. By default assumed not used (0)."));
1811 }
1812 }
1813 params[paramid - 1] = s;
1814 }
1815 }
1816 (Some(&Include), Some(&Mavlink)) => {
1817 include = PathBuf::from(s.replace('\n', ""));
1818 }
1819 (Some(&Version), Some(&Mavlink)) => {
1820 profile.version =
1821 Some(s.parse().expect("Invalid minor version number format"));
1822 }
1823 (Some(&Dialect), Some(&Mavlink)) => {
1824 profile.dialect = Some(s.parse().expect("Invalid dialect number format"));
1825 }
1826 (Some(Deprecated), _) => {
1827 deprecated.as_mut().unwrap().note = Some(s);
1828 }
1829 data => {
1830 panic!("unexpected text data {data:?} reading {s:?}");
1831 }
1832 }
1833 }
1834 Ok(Event::End(_)) => {
1835 match stack.last() {
1836 Some(&MavXmlElement::Field) => message.fields.push(field.clone()),
1837 Some(&MavXmlElement::Entry) => {
1838 entry.deprecated = deprecated;
1839 deprecated = None;
1840 mavenum.entries.push(entry.clone());
1841 }
1842 Some(&MavXmlElement::Message) => {
1843 message.deprecated = deprecated;
1844
1845 deprecated = None;
1846 is_in_extension = false;
1847 let mut not_extension_fields = message.fields.clone();
1849 let mut extension_fields = message.fields.clone();
1850
1851 not_extension_fields.retain(|field| !field.is_extension);
1852 extension_fields.retain(|field| field.is_extension);
1853
1854 not_extension_fields.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1856
1857 let mut msg = message.clone();
1859 msg.fields.clear();
1860 msg.fields.extend(not_extension_fields);
1861 msg.fields.extend(extension_fields);
1862
1863 msg.validate_unique_fields();
1865 msg.validate_field_count();
1867
1868 profile.add_message(&msg);
1869 }
1870 Some(&MavXmlElement::Enum) => {
1871 profile.add_enum(&mavenum);
1872 }
1873 Some(&MavXmlElement::Include) => {
1874 let include_file = Path::new(&definitions_dir).join(include.clone());
1875 if !parsed_files.contains(&include_file) {
1876 let included_profile =
1877 parse_profile(definitions_dir, &include, parsed_files)?;
1878 for message in included_profile.messages.values() {
1879 profile.add_message(message);
1880 }
1881 for enm in included_profile.enums.values() {
1882 profile.add_enum(enm);
1883 }
1884 if profile.version.is_none() {
1885 profile.version = included_profile.version;
1886 }
1887 }
1888 }
1889 _ => (),
1890 }
1891 stack.pop();
1892 }
1894 Err(e) => {
1895 eprintln!("Error: {e}");
1896 break;
1897 }
1898 _ => {}
1899 }
1900 }
1901
1902 Ok(profile.update_enums())
1904}
1905
1906pub fn generate<W: Write>(
1909 definitions_dir: &Path,
1910 definition_file: &Path,
1911 output_rust: &mut W,
1912) -> Result<(), BindGenError> {
1913 let mut parsed_files: HashSet<PathBuf> = HashSet::new();
1914 let profile = parse_profile(definitions_dir, definition_file, &mut parsed_files)?;
1915
1916 let dialect_name = util::to_dialect_name(definition_file);
1917
1918 let rust_tokens = profile.emit_rust(&dialect_name);
1920 writeln!(output_rust, "{rust_tokens}").unwrap();
1921
1922 Ok(())
1923}
1924
1925pub fn extra_crc(msg: &MavMessage) -> u8 {
1931 let mut crc = CRCu16::crc16mcrf4cc();
1934
1935 crc.digest(msg.name.as_bytes());
1936 crc.digest(b" ");
1937
1938 let mut f = msg.fields.clone();
1939 f.retain(|f| !f.is_extension);
1941 f.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1942 for field in &f {
1943 crc.digest(field.mavtype.primitive_type().as_bytes());
1944 crc.digest(b" ");
1945 if field.name == "mavtype" {
1946 crc.digest(b"type");
1947 } else {
1948 crc.digest(field.name.as_bytes());
1949 }
1950 crc.digest(b" ");
1951 if let MavType::Array(_, size) | MavType::CharArray(size) = field.mavtype {
1952 crc.digest(&[size as u8]);
1953 }
1954 }
1955
1956 let crcval = crc.get_crc();
1957 ((crcval & 0xFF) ^ (crcval >> 8)) as u8
1958}
1959
1960#[cfg(not(feature = "emit-extensions"))]
1961struct ExtensionFilter {
1962 pub is_in: bool,
1963}
1964
1965struct MessageFilter {
1966 pub is_in: bool,
1967 pub messages: Vec<String>,
1968}
1969
1970impl MessageFilter {
1971 pub fn new() -> Self {
1972 Self {
1973 is_in: false,
1974 messages: vec![
1975 "STORM32_GIMBAL_MANAGER_INFORMATION".to_string(),
1977 ],
1978 }
1979 }
1980}
1981
1982struct MavXmlFilter {
1983 #[cfg(not(feature = "emit-extensions"))]
1984 extension_filter: ExtensionFilter,
1985 message_filter: MessageFilter,
1986}
1987
1988impl Default for MavXmlFilter {
1989 fn default() -> Self {
1990 Self {
1991 #[cfg(not(feature = "emit-extensions"))]
1992 extension_filter: ExtensionFilter { is_in: false },
1993 message_filter: MessageFilter::new(),
1994 }
1995 }
1996}
1997
1998impl MavXmlFilter {
1999 pub fn filter(&mut self, elements: &mut Vec<Result<Event, quick_xml::Error>>) {
2000 elements.retain(|x| self.filter_extension(x) && self.filter_messages(x));
2001 }
2002
2003 #[cfg(feature = "emit-extensions")]
2004 pub fn filter_extension(&mut self, _element: &Result<Event, quick_xml::Error>) -> bool {
2005 true
2006 }
2007
2008 #[cfg(not(feature = "emit-extensions"))]
2010 pub fn filter_extension(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
2011 match element {
2012 Ok(content) => {
2013 match content {
2014 Event::Start(bytes) | Event::Empty(bytes) => {
2015 let Some(id) = identify_element(bytes.name().into_inner()) else {
2016 panic!(
2017 "unexpected element {:?}",
2018 String::from_utf8_lossy(bytes.name().into_inner())
2019 );
2020 };
2021 if id == MavXmlElement::Extensions {
2022 self.extension_filter.is_in = true;
2023 }
2024 }
2025 Event::End(bytes) => {
2026 let Some(id) = identify_element(bytes.name().into_inner()) else {
2027 panic!(
2028 "unexpected element {:?}",
2029 String::from_utf8_lossy(bytes.name().into_inner())
2030 );
2031 };
2032
2033 if id == MavXmlElement::Message {
2034 self.extension_filter.is_in = false;
2035 }
2036 }
2037 _ => {}
2038 }
2039 !self.extension_filter.is_in
2040 }
2041 Err(error) => panic!("Failed to filter XML: {error}"),
2042 }
2043 }
2044
2045 pub fn filter_messages(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
2047 match element {
2048 Ok(content) => {
2049 match content {
2050 Event::Start(bytes) | Event::Empty(bytes) => {
2051 let Some(id) = identify_element(bytes.name().into_inner()) else {
2052 panic!(
2053 "unexpected element {:?}",
2054 String::from_utf8_lossy(bytes.name().into_inner())
2055 );
2056 };
2057 if id == MavXmlElement::Message {
2058 for attr in bytes.attributes() {
2059 let attr = attr.unwrap();
2060 if attr.key.into_inner() == b"name" {
2061 let value = String::from_utf8_lossy(&attr.value).into_owned();
2062 if self.message_filter.messages.contains(&value) {
2063 self.message_filter.is_in = true;
2064 return false;
2065 }
2066 }
2067 }
2068 }
2069 }
2070 Event::End(bytes) => {
2071 let Some(id) = identify_element(bytes.name().into_inner()) else {
2072 panic!(
2073 "unexpected element {:?}",
2074 String::from_utf8_lossy(bytes.name().into_inner())
2075 );
2076 };
2077
2078 if id == MavXmlElement::Message && self.message_filter.is_in {
2079 self.message_filter.is_in = false;
2080 return false;
2081 }
2082 }
2083 _ => {}
2084 }
2085 !self.message_filter.is_in
2086 }
2087 Err(error) => panic!("Failed to filter XML: {error}"),
2088 }
2089 }
2090}
2091
2092#[inline(always)]
2093fn to_pascal_case(text: impl AsRef<[u8]>) -> String {
2094 let input = text.as_ref();
2095 let mut result = String::with_capacity(input.len());
2096 let mut capitalize = true;
2097
2098 for &b in input {
2099 if b == b'_' {
2100 capitalize = true;
2101 continue;
2102 }
2103
2104 if capitalize {
2105 result.push((b as char).to_ascii_uppercase());
2106 capitalize = false;
2107 } else {
2108 result.push((b as char).to_ascii_lowercase());
2109 }
2110 }
2111
2112 result
2113}
2114
2115#[cfg(test)]
2116mod tests {
2117 use super::*;
2118
2119 #[test]
2120 fn emits_target_id_match_arms() {
2121 let mut profile = MavProfile::default();
2123
2124 let msg_with_targets = MavMessage {
2125 id: 300,
2126 name: "COMMAND_INT".to_string(),
2127 description: None,
2128 fields: vec![
2129 MavField {
2130 mavtype: MavType::UInt8,
2131 name: "target_system".to_string(),
2132 description: None,
2133 enumtype: None,
2134 display: None,
2135 is_extension: false,
2136 },
2137 MavField {
2138 mavtype: MavType::UInt8,
2139 name: "target_component".to_string(),
2140 description: None,
2141 enumtype: None,
2142 display: None,
2143 is_extension: false,
2144 },
2145 ],
2146 deprecated: None,
2147 };
2148
2149 let msg_without_targets = MavMessage {
2150 id: 0,
2151 name: "HEARTBEAT".to_string(),
2152 description: None,
2153 fields: vec![MavField {
2154 mavtype: MavType::UInt32,
2155 name: "custom_mode".to_string(),
2156 description: None,
2157 enumtype: None,
2158 display: None,
2159 is_extension: false,
2160 }],
2161 deprecated: None,
2162 };
2163
2164 profile.add_message(&msg_with_targets);
2165 profile.add_message(&msg_without_targets);
2166
2167 let tokens = profile.emit_rust("common");
2168 let mut code = tokens.to_string();
2169 code.retain(|c| !c.is_whitespace());
2170
2171 assert!(code.contains("fntarget_system_id(&self)->Option<u8>"));
2173 assert!(code.contains("fntarget_component_id(&self)->Option<u8>"));
2174
2175 assert!(code.contains("Self::COMMAND_INT(inner)=>Some(inner.target_system)"));
2177 assert!(code.contains("Self::COMMAND_INT(inner)=>Some(inner.target_component)"));
2178
2179 assert!(!code.contains("Self::HEARTBEAT(inner)=>Some(inner.target_system)"));
2181 assert!(!code.contains("Self::HEARTBEAT(inner)=>Some(inner.target_component)"));
2182 }
2183
2184 #[test]
2185 fn validate_unique_fields_allows_unique() {
2186 let msg = MavMessage {
2187 id: 1,
2188 name: "FOO".to_string(),
2189 description: None,
2190 fields: vec![
2191 MavField {
2192 mavtype: MavType::UInt8,
2193 name: "a".to_string(),
2194 description: None,
2195 enumtype: None,
2196 display: None,
2197 is_extension: false,
2198 },
2199 MavField {
2200 mavtype: MavType::UInt16,
2201 name: "b".to_string(),
2202 description: None,
2203 enumtype: None,
2204 display: None,
2205 is_extension: false,
2206 },
2207 ],
2208 deprecated: None,
2209 };
2210 msg.validate_unique_fields();
2212 }
2213
2214 #[test]
2215 #[should_panic(expected = "Duplicate field")]
2216 fn validate_unique_fields_panics_on_duplicate() {
2217 let msg = MavMessage {
2218 id: 2,
2219 name: "BAR".to_string(),
2220 description: None,
2221 fields: vec![
2222 MavField {
2223 mavtype: MavType::UInt8,
2224 name: "target_system".to_string(),
2225 description: None,
2226 enumtype: None,
2227 display: None,
2228 is_extension: false,
2229 },
2230 MavField {
2231 mavtype: MavType::UInt8,
2232 name: "target_system".to_string(),
2233 description: None,
2234 enumtype: None,
2235 display: None,
2236 is_extension: false,
2237 },
2238 ],
2239 deprecated: None,
2240 };
2241 msg.validate_unique_fields();
2243 }
2244
2245 #[test]
2246 fn validate_field_count_ok() {
2247 let msg = MavMessage {
2248 id: 2,
2249 name: "FOO".to_string(),
2250 description: None,
2251 fields: vec![
2252 MavField {
2253 mavtype: MavType::UInt8,
2254 name: "a".to_string(),
2255 description: None,
2256 enumtype: None,
2257 display: None,
2258 is_extension: false,
2259 },
2260 MavField {
2261 mavtype: MavType::UInt8,
2262 name: "b".to_string(),
2263 description: None,
2264 enumtype: None,
2265 display: None,
2266 is_extension: false,
2267 },
2268 ],
2269 deprecated: None,
2270 };
2271 msg.validate_field_count();
2273 }
2274
2275 #[test]
2276 #[should_panic]
2277 fn validate_field_count_too_many() {
2278 let mut fields = vec![];
2279 for i in 0..65 {
2280 let field = MavField {
2281 mavtype: MavType::UInt8,
2282 name: format!("field_{i}"),
2283 description: None,
2284 enumtype: None,
2285 display: None,
2286 is_extension: false,
2287 };
2288 fields.push(field);
2289 }
2290 let msg = MavMessage {
2291 id: 2,
2292 name: "BAZ".to_string(),
2293 description: None,
2294 fields,
2295 deprecated: None,
2296 };
2297 msg.validate_field_count();
2299 }
2300
2301 #[test]
2302 #[should_panic]
2303 fn validate_field_count_empty() {
2304 let msg = MavMessage {
2305 id: 2,
2306 name: "BAM".to_string(),
2307 description: None,
2308 fields: vec![],
2309 deprecated: None,
2310 };
2311 msg.validate_field_count();
2313 }
2314}