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