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