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