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, types::CharArray};
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 matches!(field.mavtype, MavType::Array(_, _)) {
714 quote!(
715 #[cfg_attr(feature = "serde", serde(with = "serde_arrays"))]
716 #[cfg_attr(feature = "ts", ts(type = "Array<number>"))]
717 )
718 } else if matches!(field.mavtype, MavType::CharArray(_)) {
719 quote!(
720 #[cfg_attr(feature = "ts", ts(type = "string"))]
721 )
722 } else {
723 quote!()
724 };
725
726 quote! {
727 #description
728 #serde_default
729 #serde_with_attr
730 #nametype
731 }
732 })
733 .collect::<Vec<TokenStream>>();
734 (field_toks, encoded_payload_len)
735 }
736
737 #[inline(always)]
739 fn emit_description(&self) -> TokenStream {
740 let mut ts = TokenStream::new();
741 if let Some(doc) = self.description.as_ref() {
742 let doc = format!("{doc}{}", if doc.ends_with('.') { "" } else { "." });
743 let doc = URL_REGEX.replace_all(&doc, "<$1>");
745 ts.extend(quote!(#[doc = #doc]));
746 ts.extend(quote!(#[doc = ""]));
748 }
749 let id = format!("ID: {}", self.id);
750 ts.extend(quote!(#[doc = #id]));
751 ts
752 }
753
754 #[inline(always)]
755 fn emit_serialize_vars(&self) -> TokenStream {
756 let (base_fields, ext_fields): (Vec<_>, Vec<_>) =
757 self.fields.iter().partition(|f| !f.is_extension);
758 let ser_vars = base_fields.iter().map(|f| f.rust_writer());
759 let ser_ext_vars = ext_fields.iter().map(|f| f.rust_writer());
760 quote! {
761 let mut __tmp = BytesMut::new(bytes);
762
763 #[allow(clippy::absurd_extreme_comparisons)]
771 #[allow(unused_comparisons)]
772 if __tmp.remaining() < Self::ENCODED_LEN {
773 panic!(
774 "buffer is too small (need {} bytes, but got {})",
775 Self::ENCODED_LEN,
776 __tmp.remaining(),
777 )
778 }
779
780 #(#ser_vars)*
781 if matches!(version, MavlinkVersion::V2) {
782 #(#ser_ext_vars)*
783 let len = __tmp.len();
784 ::mavlink_core::utils::remove_trailing_zeroes(&bytes[..len])
785 } else {
786 __tmp.len()
787 }
788 }
789 }
790
791 #[inline(always)]
792 fn emit_deserialize_vars(&self) -> TokenStream {
793 let deser_vars = self
794 .fields
795 .iter()
796 .map(|f| f.rust_reader())
797 .collect::<Vec<TokenStream>>();
798
799 if deser_vars.is_empty() {
800 quote! {
802 Ok(Self::default())
803 }
804 } else {
805 quote! {
806 let avail_len = __input.len();
807
808 let mut payload_buf = [0; Self::ENCODED_LEN];
809 let mut buf = if avail_len < Self::ENCODED_LEN {
810 payload_buf[0..avail_len].copy_from_slice(__input);
812 Bytes::new(&payload_buf)
813 } else {
814 Bytes::new(__input)
816 };
817
818 let mut __struct = Self::default();
819 #(#deser_vars)*
820 Ok(__struct)
821 }
822 }
823 }
824
825 #[inline(always)]
826 fn emit_default_impl(&self) -> TokenStream {
827 let msg_name = self.emit_struct_name();
828 quote! {
829 impl Default for #msg_name {
830 fn default() -> Self {
831 Self::DEFAULT.clone()
832 }
833 }
834 }
835 }
836
837 #[inline(always)]
838 fn emit_deprecation(&self) -> TokenStream {
839 self.deprecated
840 .as_ref()
841 .map(|d| d.emit_tokens())
842 .unwrap_or_default()
843 }
844
845 #[inline(always)]
846 fn emit_const_default(&self, dialect_has_version: bool) -> TokenStream {
847 let initializers = self
848 .fields
849 .iter()
850 .map(|field| field.emit_default_initializer(dialect_has_version));
851 quote!(pub const DEFAULT: Self = Self { #(#initializers)* };)
852 }
853
854 fn emit_rust(&self, dialect_has_version: bool) -> TokenStream {
855 let msg_name = self.emit_struct_name();
856 let id = self.id;
857 let name = self.name.clone();
858 let extra_crc = extra_crc(self);
859 let (name_types, payload_encoded_len) = self.emit_name_types();
860 assert!(
861 payload_encoded_len <= 255,
862 "maximum payload length is 255 bytes"
863 );
864
865 let deser_vars = self.emit_deserialize_vars();
866 let serialize_vars = self.emit_serialize_vars();
867 let const_default = self.emit_const_default(dialect_has_version);
868 let default_impl = self.emit_default_impl();
869
870 let deprecation = self.emit_deprecation();
871
872 let description = self.emit_description();
873
874 quote! {
875 #deprecation
876 #description
877 #[derive(Debug, Clone, PartialEq)]
878 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
879 #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
880 #[cfg_attr(feature = "ts", derive(TS))]
881 #[cfg_attr(feature = "ts", ts(export))]
882 pub struct #msg_name {
883 #(#name_types)*
884 }
885
886 impl #msg_name {
887 pub const ENCODED_LEN: usize = #payload_encoded_len;
888 #const_default
889
890 #[cfg(feature = "arbitrary")]
891 pub fn random<R: rand::RngCore>(rng: &mut R) -> Self {
892 use arbitrary::{Unstructured, Arbitrary};
893 let mut buf = [0u8; 1024];
894 rng.fill_bytes(&mut buf);
895 let mut unstructured = Unstructured::new(&buf);
896 Self::arbitrary(&mut unstructured).unwrap_or_default()
897 }
898 }
899
900 #default_impl
901
902 impl MessageData for #msg_name {
903 type Message = MavMessage;
904
905 const ID: u32 = #id;
906 const NAME: &'static str = #name;
907 const EXTRA_CRC: u8 = #extra_crc;
908 const ENCODED_LEN: usize = #payload_encoded_len;
909
910 fn deser(_version: MavlinkVersion, __input: &[u8]) -> Result<Self, ::mavlink_core::error::ParserError> {
911 #deser_vars
912 }
913
914 fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize {
915 #serialize_vars
916 }
917 }
918 }
919 }
920
921 fn validate_unique_fields(&self) {
925 let mut seen: HashSet<&str> = HashSet::new();
926 for f in &self.fields {
927 let name: &str = &f.name;
928 assert!(
929 seen.insert(name),
930 "Duplicate field '{}' found in message '{}' while generating bindings",
931 name,
932 self.name
933 );
934 }
935 }
936
937 fn validate_field_count(&self) {
939 assert!(
940 !self.fields.is_empty(),
941 "Message '{}' does not any fields",
942 self.name
943 );
944 assert!(
945 self.fields.len() <= 64,
946 "Message '{}' has more then 64 fields",
947 self.name
948 );
949 }
950}
951
952#[derive(Debug, PartialEq, Clone, Default)]
953#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
954pub struct MavField {
955 pub mavtype: MavType,
956 pub name: String,
957 pub description: Option<String>,
958 pub enumtype: Option<String>,
959 pub display: Option<String>,
960 pub is_extension: bool,
961}
962
963impl MavField {
964 #[inline(always)]
966 fn emit_name(&self) -> TokenStream {
967 let name = format_ident!("{}", self.name);
968 quote!(#name)
969 }
970
971 #[inline(always)]
973 fn emit_type(&self) -> TokenStream {
974 let mavtype;
975 if matches!(self.mavtype, MavType::Array(_, _)) {
976 let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
977 mavtype = quote!(#rt);
978 } else if let Some(enumname) = &self.enumtype {
979 let en = TokenStream::from_str(enumname).unwrap();
980 mavtype = quote!(#en);
981 } else {
982 let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
983 mavtype = quote!(#rt);
984 }
985 mavtype
986 }
987
988 #[inline(always)]
990 fn emit_description(&self) -> TokenStream {
991 let mut ts = TokenStream::new();
992 if let Some(val) = self.description.as_ref() {
993 let desc = URL_REGEX.replace_all(val, "<$1>");
994 ts.extend(quote!(#[doc = #desc]));
995 }
996 ts
997 }
998
999 #[inline(always)]
1001 fn emit_name_type(&self) -> TokenStream {
1002 let name = self.emit_name();
1003 let fieldtype = self.emit_type();
1004 quote!(pub #name: #fieldtype,)
1005 }
1006
1007 fn rust_writer(&self) -> TokenStream {
1009 let mut name = "self.".to_string() + &self.name.clone();
1010 if self.enumtype.is_some() {
1011 if !matches!(self.mavtype, MavType::Array(_, _)) {
1014 if let Some(dsp) = &self.display {
1015 if dsp == "bitmask" {
1017 name += ".bits()";
1019 } else {
1020 panic!("Display option not implemented");
1021 }
1022 } else {
1023 name += " as ";
1025 name += &self.mavtype.rust_type();
1026 }
1027 }
1028 }
1029 let ts = TokenStream::from_str(&name).unwrap();
1030 let name = quote!(#ts);
1031 let buf = format_ident!("__tmp");
1032 self.mavtype.rust_writer(&name, buf)
1033 }
1034
1035 fn rust_reader(&self) -> TokenStream {
1037 let _name = TokenStream::from_str(&self.name).unwrap();
1038
1039 let name = quote!(__struct.#_name);
1040 let buf = format_ident!("buf");
1041 if let Some(enum_name) = &self.enumtype {
1042 if let MavType::Array(_t, _size) = &self.mavtype {
1045 return self.mavtype.rust_reader(&name, buf);
1046 }
1047 if let Some(dsp) = &self.display {
1048 if dsp == "bitmask" {
1049 let tmp = self.mavtype.rust_reader("e!(let tmp), buf);
1051 let enum_name_ident = format_ident!("{}", enum_name);
1052 quote! {
1053 #tmp
1054 #name = #enum_name_ident::from_bits(tmp & #enum_name_ident::all().bits())
1055 .ok_or(::mavlink_core::error::ParserError::InvalidFlag { flag_type: #enum_name, value: tmp as u32 })?;
1056 }
1057 } else {
1058 panic!("Display option not implemented");
1059 }
1060 } else {
1061 let tmp = self.mavtype.rust_reader("e!(let tmp), buf);
1063 let val = format_ident!("from_{}", &self.mavtype.rust_type());
1064 quote!(
1065 #tmp
1066 #name = FromPrimitive::#val(tmp)
1067 .ok_or(::mavlink_core::error::ParserError::InvalidEnum { enum_type: #enum_name, value: tmp as u32 })?;
1068 )
1069 }
1070 } else {
1071 self.mavtype.rust_reader(&name, buf)
1072 }
1073 }
1074
1075 #[inline(always)]
1076 fn emit_default_initializer(&self, dialect_has_version: bool) -> TokenStream {
1077 let field = self.emit_name();
1078 if matches!(self.mavtype, MavType::Array(_, _)) {
1080 let default_value = self.mavtype.emit_default_value(dialect_has_version);
1081 quote!(#field: #default_value,)
1082 } else if let Some(enumname) = &self.enumtype {
1083 let ty = TokenStream::from_str(enumname).unwrap();
1084 quote!(#field: #ty::DEFAULT,)
1085 } else {
1086 let default_value = self.mavtype.emit_default_value(dialect_has_version);
1087 quote!(#field: #default_value,)
1088 }
1089 }
1090}
1091
1092#[derive(Debug, PartialEq, Clone, Default)]
1093#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1094pub enum MavType {
1095 UInt8MavlinkVersion,
1096 #[default]
1097 UInt8,
1098 UInt16,
1099 UInt32,
1100 UInt64,
1101 Int8,
1102 Int16,
1103 Int32,
1104 Int64,
1105 Char,
1106 Float,
1107 Double,
1108 CharArray(usize),
1109 Array(Box<MavType>, usize),
1110}
1111
1112impl MavType {
1113 fn parse_type(s: &str) -> Option<Self> {
1114 use self::MavType::*;
1115 match s {
1116 "uint8_t_mavlink_version" => Some(UInt8MavlinkVersion),
1117 "uint8_t" => Some(UInt8),
1118 "uint16_t" => Some(UInt16),
1119 "uint32_t" => Some(UInt32),
1120 "uint64_t" => Some(UInt64),
1121 "int8_t" => Some(Int8),
1122 "int16_t" => Some(Int16),
1123 "int32_t" => Some(Int32),
1124 "int64_t" => Some(Int64),
1125 "char" => Some(Char),
1126 "float" => Some(Float),
1127 "Double" => Some(Double),
1128 "double" => Some(Double),
1129 _ if s.starts_with("char[") => {
1130 let start = 4;
1131 let size = s[start + 1..(s.len() - 1)].parse::<usize>().ok()?;
1132 Some(CharArray(size))
1133 }
1134 _ if s.ends_with(']') => {
1135 let start = s.find('[')?;
1136 let size = s[start + 1..(s.len() - 1)].parse::<usize>().ok()?;
1137 let mtype = Self::parse_type(&s[0..start])?;
1138 Some(Array(Box::new(mtype), size))
1139 }
1140 _ => None,
1141 }
1142 }
1143
1144 pub fn rust_reader(&self, val: &TokenStream, buf: Ident) -> TokenStream {
1146 use self::MavType::*;
1147 match self {
1148 Char => quote! {#val = #buf.get_u8();},
1149 UInt8 => quote! {#val = #buf.get_u8();},
1150 UInt16 => quote! {#val = #buf.get_u16_le();},
1151 UInt32 => quote! {#val = #buf.get_u32_le();},
1152 UInt64 => quote! {#val = #buf.get_u64_le();},
1153 UInt8MavlinkVersion => quote! {#val = #buf.get_u8();},
1154 Int8 => quote! {#val = #buf.get_i8();},
1155 Int16 => quote! {#val = #buf.get_i16_le();},
1156 Int32 => quote! {#val = #buf.get_i32_le();},
1157 Int64 => quote! {#val = #buf.get_i64_le();},
1158 Float => quote! {#val = #buf.get_f32_le();},
1159 Double => quote! {#val = #buf.get_f64_le();},
1160 CharArray(size) => {
1161 quote! {
1162 let mut tmp = [0_u8; #size];
1163 for v in &mut tmp {
1164 *v = #buf.get_u8();
1165 }
1166 #val = CharArray::new(tmp);
1167 }
1168 }
1169 Array(t, _) => {
1170 let r = t.rust_reader("e!(let val), buf);
1171 quote! {
1172 for v in &mut #val {
1173 #r
1174 *v = val;
1175 }
1176 }
1177 }
1178 }
1179 }
1180
1181 pub fn rust_writer(&self, val: &TokenStream, buf: Ident) -> TokenStream {
1183 use self::MavType::*;
1184 match self {
1185 UInt8MavlinkVersion => quote! {#buf.put_u8(#val);},
1186 UInt8 => quote! {#buf.put_u8(#val);},
1187 Char => quote! {#buf.put_u8(#val);},
1188 UInt16 => quote! {#buf.put_u16_le(#val);},
1189 UInt32 => quote! {#buf.put_u32_le(#val);},
1190 Int8 => quote! {#buf.put_i8(#val);},
1191 Int16 => quote! {#buf.put_i16_le(#val);},
1192 Int32 => quote! {#buf.put_i32_le(#val);},
1193 Float => quote! {#buf.put_f32_le(#val);},
1194 UInt64 => quote! {#buf.put_u64_le(#val);},
1195 Int64 => quote! {#buf.put_i64_le(#val);},
1196 Double => quote! {#buf.put_f64_le(#val);},
1197 CharArray(_) => {
1198 let w = Char.rust_writer("e!(*val), buf);
1199 quote! {
1200 for val in &#val {
1201 #w
1202 }
1203 }
1204 }
1205 Array(t, _size) => {
1206 let w = t.rust_writer("e!(*val), buf);
1207 quote! {
1208 for val in &#val {
1209 #w
1210 }
1211 }
1212 }
1213 }
1214 }
1215
1216 fn 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 CharArray(size) => *size,
1225 Array(t, size) => t.len() * size,
1226 }
1227 }
1228
1229 fn order_len(&self) -> usize {
1231 use self::MavType::*;
1232 match self {
1233 UInt8MavlinkVersion | UInt8 | Int8 | Char | CharArray(_) => 1,
1234 UInt16 | Int16 => 2,
1235 UInt32 | Int32 | Float => 4,
1236 UInt64 | Int64 | Double => 8,
1237 Array(t, _) => t.len(),
1238 }
1239 }
1240
1241 pub fn primitive_type(&self) -> String {
1243 use self::MavType::*;
1244 match self {
1245 UInt8MavlinkVersion => "uint8_t".into(),
1246 UInt8 => "uint8_t".into(),
1247 Int8 => "int8_t".into(),
1248 Char => "char".into(),
1249 UInt16 => "uint16_t".into(),
1250 Int16 => "int16_t".into(),
1251 UInt32 => "uint32_t".into(),
1252 Int32 => "int32_t".into(),
1253 Float => "float".into(),
1254 UInt64 => "uint64_t".into(),
1255 Int64 => "int64_t".into(),
1256 Double => "double".into(),
1257 CharArray(_) => "char".into(),
1258 Array(t, _) => t.primitive_type(),
1259 }
1260 }
1261
1262 pub fn rust_type(&self) -> String {
1265 use self::MavType::*;
1266 match self {
1267 UInt8 | UInt8MavlinkVersion => "u8".into(),
1268 Int8 => "i8".into(),
1269 Char => "u8".into(),
1270 UInt16 => "u16".into(),
1271 Int16 => "i16".into(),
1272 UInt32 => "u32".into(),
1273 Int32 => "i32".into(),
1274 Float => "f32".into(),
1275 UInt64 => "u64".into(),
1276 Int64 => "i64".into(),
1277 Double => "f64".into(),
1278 CharArray(size) => format!("CharArray<{}>", size),
1279 Array(t, size) => format!("[{};{}]", t.rust_type(), size),
1280 }
1281 }
1282
1283 pub fn emit_default_value(&self, dialect_has_version: bool) -> TokenStream {
1284 use self::MavType::*;
1285 match self {
1286 UInt8 => quote!(0_u8),
1287 UInt8MavlinkVersion => {
1288 if dialect_has_version {
1289 quote!(MINOR_MAVLINK_VERSION)
1290 } else {
1291 quote!(0_u8)
1292 }
1293 }
1294 Int8 => quote!(0_i8),
1295 Char => quote!(0_u8),
1296 UInt16 => quote!(0_u16),
1297 Int16 => quote!(0_i16),
1298 UInt32 => quote!(0_u32),
1299 Int32 => quote!(0_i32),
1300 Float => quote!(0.0_f32),
1301 UInt64 => quote!(0_u64),
1302 Int64 => quote!(0_i64),
1303 Double => quote!(0.0_f64),
1304 CharArray(size) => quote!(CharArray::new([0_u8; #size])),
1305 Array(ty, size) => {
1306 let default_value = ty.emit_default_value(dialect_has_version);
1307 quote!([#default_value; #size])
1308 }
1309 }
1310 }
1311
1312 pub fn rust_primitive_type(&self) -> String {
1316 use self::MavType::*;
1317 match self {
1318 Array(t, _) => t.rust_primitive_type(),
1319 _ => self.rust_type(),
1320 }
1321 }
1322
1323 pub fn compare(&self, other: &Self) -> Ordering {
1325 let len = self.order_len();
1326 (-(len as isize)).cmp(&(-(other.order_len() as isize)))
1327 }
1328}
1329
1330#[derive(Debug, PartialEq, Eq, Clone, Default)]
1331#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1332pub struct MavDeprecation {
1333 pub since: String,
1335 pub replaced_by: String,
1337 pub note: Option<String>,
1338}
1339
1340impl MavDeprecation {
1341 pub fn emit_tokens(&self) -> TokenStream {
1342 let since = &self.since;
1343 let note = match &self.note {
1344 Some(str) if str.is_empty() || str.ends_with(".") => str.clone(),
1345 Some(str) => format!("{str}."),
1346 None => String::new(),
1347 };
1348 let replaced_by = if self.replaced_by.starts_with("`") {
1349 format!("See {}", self.replaced_by)
1350 } else if self.replaced_by.is_empty() {
1351 String::new()
1352 } else {
1353 format!("See `{}`", self.replaced_by)
1354 };
1355 let message = format!("{note} {replaced_by} (Deprecated since {since})");
1356 quote!(#[deprecated = #message])
1357 }
1358}
1359
1360#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1361#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1362#[cfg_attr(feature = "serde", serde(tag = "type"))]
1363pub enum MavXmlElement {
1364 Version,
1365 Mavlink,
1366 Dialect,
1367 Include,
1368 Enums,
1369 Enum,
1370 Entry,
1371 Description,
1372 Param,
1373 Messages,
1374 Message,
1375 Field,
1376 Deprecated,
1377 Wip,
1378 Extensions,
1379}
1380
1381const fn identify_element(s: &[u8]) -> Option<MavXmlElement> {
1382 use self::MavXmlElement::*;
1383 match s {
1384 b"version" => Some(Version),
1385 b"mavlink" => Some(Mavlink),
1386 b"dialect" => Some(Dialect),
1387 b"include" => Some(Include),
1388 b"enums" => Some(Enums),
1389 b"enum" => Some(Enum),
1390 b"entry" => Some(Entry),
1391 b"description" => Some(Description),
1392 b"param" => Some(Param),
1393 b"messages" => Some(Messages),
1394 b"message" => Some(Message),
1395 b"field" => Some(Field),
1396 b"deprecated" => Some(Deprecated),
1397 b"wip" => Some(Wip),
1398 b"extensions" => Some(Extensions),
1399 _ => None,
1400 }
1401}
1402
1403fn is_valid_parent(p: Option<MavXmlElement>, s: MavXmlElement) -> bool {
1404 use self::MavXmlElement::*;
1405 match s {
1406 Version => p == Some(Mavlink),
1407 Mavlink => p.is_none(),
1408 Dialect => p == Some(Mavlink),
1409 Include => p == Some(Mavlink),
1410 Enums => p == Some(Mavlink),
1411 Enum => p == Some(Enums),
1412 Entry => p == Some(Enum),
1413 Description => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1414 Param => p == Some(Entry),
1415 Messages => p == Some(Mavlink),
1416 Message => p == Some(Messages),
1417 Field => p == Some(Message),
1418 Deprecated => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1419 Wip => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1420 Extensions => p == Some(Message),
1421 }
1422}
1423
1424pub fn parse_profile(
1425 definitions_dir: &Path,
1426 definition_file: &Path,
1427 parsed_files: &mut HashSet<PathBuf>,
1428) -> Result<MavProfile, BindGenError> {
1429 let in_path = Path::new(&definitions_dir).join(definition_file);
1430 parsed_files.insert(in_path.clone()); let mut stack: Vec<MavXmlElement> = vec![];
1433
1434 let mut profile = MavProfile::default();
1435 let mut field = MavField::default();
1436 let mut message = MavMessage::default();
1437 let mut mavenum = MavEnum::default();
1438 let mut entry = MavEnumEntry::default();
1439 let mut include = PathBuf::new();
1440 let mut paramid: Option<usize> = None;
1441 let mut deprecated: Option<MavDeprecation> = None;
1442
1443 let mut xml_filter = MavXmlFilter::default();
1444 let mut events: Vec<Result<Event, quick_xml::Error>> = Vec::new();
1445 let file = File::open(&in_path).map_err(|e| BindGenError::CouldNotReadDefinitionFile {
1446 source: e,
1447 path: in_path.clone(),
1448 })?;
1449 let mut reader = Reader::from_reader(BufReader::new(file));
1450 reader.config_mut().trim_text(true);
1451
1452 let mut buf = Vec::new();
1453 loop {
1454 match reader.read_event_into(&mut buf) {
1455 Ok(Event::Eof) => {
1456 events.push(Ok(Event::Eof));
1457 break;
1458 }
1459 Ok(event) => events.push(Ok(event.into_owned())),
1460 Err(why) => events.push(Err(why)),
1461 }
1462 buf.clear();
1463 }
1464 xml_filter.filter(&mut events);
1465 let mut is_in_extension = false;
1466 for e in events {
1467 match e {
1468 Ok(Event::Start(bytes)) => {
1469 let Some(id) = identify_element(bytes.name().into_inner()) else {
1470 panic!(
1471 "unexpected element {:?}",
1472 String::from_utf8_lossy(bytes.name().into_inner())
1473 );
1474 };
1475
1476 assert!(
1477 is_valid_parent(stack.last().copied(), id),
1478 "not valid parent {:?} of {id:?}",
1479 stack.last(),
1480 );
1481
1482 match id {
1483 MavXmlElement::Extensions => {
1484 is_in_extension = true;
1485 }
1486 MavXmlElement::Message => {
1487 message = MavMessage::default();
1488 }
1489 MavXmlElement::Field => {
1490 field = MavField {
1491 is_extension: is_in_extension,
1492 ..Default::default()
1493 };
1494 }
1495 MavXmlElement::Enum => {
1496 mavenum = MavEnum::default();
1497 }
1498 MavXmlElement::Entry => {
1499 if mavenum.entries.is_empty() {
1500 mavenum.deprecated = deprecated;
1501 }
1502 deprecated = None;
1503 entry = MavEnumEntry::default();
1504 }
1505 MavXmlElement::Include => {
1506 include = PathBuf::default();
1507 }
1508 MavXmlElement::Param => {
1509 paramid = None;
1510 }
1511 MavXmlElement::Deprecated => {
1512 deprecated = Some(MavDeprecation {
1513 replaced_by: String::new(),
1514 since: String::new(),
1515 note: None,
1516 });
1517 }
1518 _ => (),
1519 }
1520 stack.push(id);
1521
1522 for attr in bytes.attributes() {
1523 let attr = attr.unwrap();
1524 match stack.last() {
1525 Some(&MavXmlElement::Enum) => {
1526 if attr.key.into_inner() == b"name" {
1527 mavenum.name = to_pascal_case(attr.value);
1528 } else if attr.key.into_inner() == b"bitmask" {
1530 mavenum.bitmask = true;
1531 }
1532 }
1533 Some(&MavXmlElement::Entry) => {
1534 match attr.key.into_inner() {
1535 b"name" => {
1536 entry.name = String::from_utf8_lossy(&attr.value).to_string();
1537 }
1538 b"value" => {
1539 let value = String::from_utf8_lossy(&attr.value);
1540 let (src, radix) = value
1542 .strip_prefix("0x")
1543 .map(|value| (value, 16))
1544 .unwrap_or((value.as_ref(), 10));
1545 entry.value = u32::from_str_radix(src, radix).ok();
1546 }
1547 _ => (),
1548 }
1549 }
1550 Some(&MavXmlElement::Message) => {
1551 match attr.key.into_inner() {
1552 b"name" => {
1553 message.name = String::from_utf8_lossy(&attr.value).to_string();
1567 }
1568 b"id" => {
1569 message.id =
1570 String::from_utf8_lossy(&attr.value).parse().unwrap();
1571 }
1572 _ => (),
1573 }
1574 }
1575 Some(&MavXmlElement::Field) => {
1576 match attr.key.into_inner() {
1577 b"name" => {
1578 let name = String::from_utf8_lossy(&attr.value);
1579 field.name = if name == "type" {
1580 "mavtype".to_string()
1581 } else {
1582 name.to_string()
1583 };
1584 }
1585 b"type" => {
1586 let r#type = String::from_utf8_lossy(&attr.value);
1587 field.mavtype = MavType::parse_type(&r#type).unwrap();
1588 }
1589 b"enum" => {
1590 field.enumtype = Some(to_pascal_case(&attr.value));
1591
1592 if let Some(e) =
1594 profile.enums.get(field.enumtype.as_ref().unwrap())
1595 {
1596 if e.bitmask {
1597 field.display = Some("bitmask".to_string());
1598 }
1599 }
1600 }
1601 b"display" => {
1602 field.display =
1603 Some(String::from_utf8_lossy(&attr.value).to_string());
1604 }
1605 _ => (),
1606 }
1607 }
1608 Some(&MavXmlElement::Param) => {
1609 if entry.params.is_none() {
1610 entry.params = Some(vec![]);
1611 }
1612 if attr.key.into_inner() == b"index" {
1613 paramid =
1614 Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1615 }
1616 }
1617 Some(&MavXmlElement::Deprecated) => match attr.key.into_inner() {
1618 b"since" => {
1619 deprecated.as_mut().unwrap().since =
1620 String::from_utf8_lossy(&attr.value).to_string();
1621 }
1622 b"replaced_by" => {
1623 deprecated.as_mut().unwrap().replaced_by =
1624 String::from_utf8_lossy(&attr.value).to_string();
1625 }
1626 _ => (),
1627 },
1628 _ => (),
1629 }
1630 }
1631 }
1632 Ok(Event::Empty(bytes)) => match bytes.name().into_inner() {
1633 b"extensions" => {
1634 is_in_extension = true;
1635 }
1636 b"entry" => {
1637 if mavenum.entries.is_empty() {
1638 mavenum.deprecated = deprecated;
1639 }
1640 deprecated = None;
1641 entry = MavEnumEntry::default();
1642 for attr in bytes.attributes() {
1643 let attr = attr.unwrap();
1644 match attr.key.into_inner() {
1645 b"name" => {
1646 entry.name = String::from_utf8_lossy(&attr.value).to_string();
1647 }
1648 b"value" => {
1649 entry.value =
1650 Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1651 }
1652 _ => (),
1653 }
1654 }
1655 mavenum.entries.push(entry.clone());
1656 }
1657 b"deprecated" => {
1658 deprecated = Some(MavDeprecation {
1659 since: String::new(),
1660 replaced_by: String::new(),
1661 note: None,
1662 });
1663 for attr in bytes.attributes() {
1664 let attr = attr.unwrap();
1665 match attr.key.into_inner() {
1666 b"since" => {
1667 deprecated.as_mut().unwrap().since =
1668 String::from_utf8_lossy(&attr.value).to_string();
1669 }
1670 b"replaced_by" => {
1671 deprecated.as_mut().unwrap().replaced_by =
1672 String::from_utf8_lossy(&attr.value).to_string();
1673 }
1674 _ => (),
1675 }
1676 }
1677 }
1678 b"field" => {
1679 let mut field = MavField {
1680 is_extension: is_in_extension,
1681 ..Default::default()
1682 };
1683 for attr in bytes.attributes() {
1684 let attr = attr.unwrap();
1685 match attr.key.into_inner() {
1686 b"name" => {
1687 let name = String::from_utf8_lossy(&attr.value);
1688 field.name = if name == "type" {
1689 "mavtype".to_string()
1690 } else {
1691 name.to_string()
1692 };
1693 }
1694 b"type" => {
1695 let r#type = String::from_utf8_lossy(&attr.value);
1696 field.mavtype = MavType::parse_type(&r#type).unwrap();
1697 }
1698 b"enum" => {
1699 field.enumtype = Some(to_pascal_case(&attr.value));
1700
1701 if let Some(e) = profile.enums.get(field.enumtype.as_ref().unwrap())
1703 {
1704 if e.bitmask {
1705 field.display = Some("bitmask".to_string());
1706 }
1707 }
1708 }
1709 b"display" => {
1710 field.display =
1711 Some(String::from_utf8_lossy(&attr.value).to_string());
1712 }
1713 _ => (),
1714 }
1715 }
1716 message.fields.push(field);
1717 }
1718 _ => (),
1719 },
1720 Ok(Event::Text(bytes)) => {
1721 let s = String::from_utf8_lossy(&bytes).to_string();
1722
1723 use self::MavXmlElement::*;
1724 match (stack.last(), stack.get(stack.len() - 2)) {
1725 (Some(&Description), Some(&Message)) => {
1726 message.description = Some(s.replace('\n', " "));
1727 }
1728 (Some(&Field), Some(&Message)) => {
1729 field.description = Some(s.replace('\n', " "));
1730 }
1731 (Some(&Description), Some(&Enum)) => {
1732 mavenum.description = Some(s.replace('\n', " "));
1733 }
1734 (Some(&Description), Some(&Entry)) => {
1735 entry.description = Some(s.replace('\n', " "));
1736 }
1737 (Some(&Param), Some(&Entry)) => {
1738 if let Some(params) = entry.params.as_mut() {
1739 let paramid = paramid.unwrap();
1742 if params.len() < paramid {
1743 for index in params.len()..paramid {
1744 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)."));
1745 }
1746 }
1747 params[paramid - 1] = s;
1748 }
1749 }
1750 (Some(&Include), Some(&Mavlink)) => {
1751 include = PathBuf::from(s.replace('\n', ""));
1752 }
1753 (Some(&Version), Some(&Mavlink)) => {
1754 profile.version =
1755 Some(s.parse().expect("Invalid minor version number format"));
1756 }
1757 (Some(&Dialect), Some(&Mavlink)) => {
1758 profile.dialect = Some(s.parse().expect("Invalid dialect number format"));
1759 }
1760 (Some(Deprecated), _) => {
1761 deprecated.as_mut().unwrap().note = Some(s);
1762 }
1763 data => {
1764 panic!("unexpected text data {data:?} reading {s:?}");
1765 }
1766 }
1767 }
1768 Ok(Event::End(_)) => {
1769 match stack.last() {
1770 Some(&MavXmlElement::Field) => message.fields.push(field.clone()),
1771 Some(&MavXmlElement::Entry) => {
1772 entry.deprecated = deprecated;
1773 deprecated = None;
1774 mavenum.entries.push(entry.clone());
1775 }
1776 Some(&MavXmlElement::Message) => {
1777 message.deprecated = deprecated;
1778
1779 deprecated = None;
1780 is_in_extension = false;
1781 let mut not_extension_fields = message.fields.clone();
1783 let mut extension_fields = message.fields.clone();
1784
1785 not_extension_fields.retain(|field| !field.is_extension);
1786 extension_fields.retain(|field| field.is_extension);
1787
1788 not_extension_fields.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1790
1791 let mut msg = message.clone();
1793 msg.fields.clear();
1794 msg.fields.extend(not_extension_fields);
1795 msg.fields.extend(extension_fields);
1796
1797 msg.validate_unique_fields();
1799 msg.validate_field_count();
1801
1802 profile.add_message(&msg);
1803 }
1804 Some(&MavXmlElement::Enum) => {
1805 profile.add_enum(&mavenum);
1806 }
1807 Some(&MavXmlElement::Include) => {
1808 let include_file = Path::new(&definitions_dir).join(include.clone());
1809 if !parsed_files.contains(&include_file) {
1810 let included_profile =
1811 parse_profile(definitions_dir, &include, parsed_files)?;
1812 for message in included_profile.messages.values() {
1813 profile.add_message(message);
1814 }
1815 for enm in included_profile.enums.values() {
1816 profile.add_enum(enm);
1817 }
1818 if profile.version.is_none() {
1819 profile.version = included_profile.version;
1820 }
1821 }
1822 }
1823 _ => (),
1824 }
1825 stack.pop();
1826 }
1828 Err(e) => {
1829 eprintln!("Error: {e}");
1830 break;
1831 }
1832 _ => {}
1833 }
1834 }
1835
1836 Ok(profile.update_enums())
1838}
1839
1840pub fn generate<W: Write>(
1843 definitions_dir: &Path,
1844 definition_file: &Path,
1845 output_rust: &mut W,
1846) -> Result<(), BindGenError> {
1847 let mut parsed_files: HashSet<PathBuf> = HashSet::new();
1848 let profile = parse_profile(definitions_dir, definition_file, &mut parsed_files)?;
1849
1850 let dialect_name = util::to_dialect_name(definition_file);
1851
1852 let rust_tokens = profile.emit_rust(&dialect_name);
1854 writeln!(output_rust, "{rust_tokens}").unwrap();
1855
1856 Ok(())
1857}
1858
1859pub fn extra_crc(msg: &MavMessage) -> u8 {
1865 let mut crc = CRCu16::crc16mcrf4cc();
1868
1869 crc.digest(msg.name.as_bytes());
1870 crc.digest(b" ");
1871
1872 let mut f = msg.fields.clone();
1873 f.retain(|f| !f.is_extension);
1875 f.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1876 for field in &f {
1877 crc.digest(field.mavtype.primitive_type().as_bytes());
1878 crc.digest(b" ");
1879 if field.name == "mavtype" {
1880 crc.digest(b"type");
1881 } else {
1882 crc.digest(field.name.as_bytes());
1883 }
1884 crc.digest(b" ");
1885 if let MavType::Array(_, size) | MavType::CharArray(size) = field.mavtype {
1886 crc.digest(&[size as u8]);
1887 }
1888 }
1889
1890 let crcval = crc.get_crc();
1891 ((crcval & 0xFF) ^ (crcval >> 8)) as u8
1892}
1893
1894#[cfg(not(feature = "emit-extensions"))]
1895struct ExtensionFilter {
1896 pub is_in: bool,
1897}
1898
1899struct MessageFilter {
1900 pub is_in: bool,
1901 pub messages: Vec<String>,
1902}
1903
1904impl MessageFilter {
1905 pub fn new() -> Self {
1906 Self {
1907 is_in: false,
1908 messages: vec![
1909 "STORM32_GIMBAL_MANAGER_INFORMATION".to_string(),
1911 ],
1912 }
1913 }
1914}
1915
1916struct MavXmlFilter {
1917 #[cfg(not(feature = "emit-extensions"))]
1918 extension_filter: ExtensionFilter,
1919 message_filter: MessageFilter,
1920}
1921
1922impl Default for MavXmlFilter {
1923 fn default() -> Self {
1924 Self {
1925 #[cfg(not(feature = "emit-extensions"))]
1926 extension_filter: ExtensionFilter { is_in: false },
1927 message_filter: MessageFilter::new(),
1928 }
1929 }
1930}
1931
1932impl MavXmlFilter {
1933 pub fn filter(&mut self, elements: &mut Vec<Result<Event, quick_xml::Error>>) {
1934 elements.retain(|x| self.filter_extension(x) && self.filter_messages(x));
1935 }
1936
1937 #[cfg(feature = "emit-extensions")]
1938 pub fn filter_extension(&mut self, _element: &Result<Event, quick_xml::Error>) -> bool {
1939 true
1940 }
1941
1942 #[cfg(not(feature = "emit-extensions"))]
1944 pub fn filter_extension(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
1945 match element {
1946 Ok(content) => {
1947 match content {
1948 Event::Start(bytes) | Event::Empty(bytes) => {
1949 let Some(id) = identify_element(bytes.name().into_inner()) else {
1950 panic!(
1951 "unexpected element {:?}",
1952 String::from_utf8_lossy(bytes.name().into_inner())
1953 );
1954 };
1955 if id == MavXmlElement::Extensions {
1956 self.extension_filter.is_in = true;
1957 }
1958 }
1959 Event::End(bytes) => {
1960 let Some(id) = identify_element(bytes.name().into_inner()) else {
1961 panic!(
1962 "unexpected element {:?}",
1963 String::from_utf8_lossy(bytes.name().into_inner())
1964 );
1965 };
1966
1967 if id == MavXmlElement::Message {
1968 self.extension_filter.is_in = false;
1969 }
1970 }
1971 _ => {}
1972 }
1973 !self.extension_filter.is_in
1974 }
1975 Err(error) => panic!("Failed to filter XML: {error}"),
1976 }
1977 }
1978
1979 pub fn filter_messages(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
1981 match element {
1982 Ok(content) => {
1983 match content {
1984 Event::Start(bytes) | Event::Empty(bytes) => {
1985 let Some(id) = identify_element(bytes.name().into_inner()) else {
1986 panic!(
1987 "unexpected element {:?}",
1988 String::from_utf8_lossy(bytes.name().into_inner())
1989 );
1990 };
1991 if id == MavXmlElement::Message {
1992 for attr in bytes.attributes() {
1993 let attr = attr.unwrap();
1994 if attr.key.into_inner() == b"name" {
1995 let value = String::from_utf8_lossy(&attr.value).into_owned();
1996 if self.message_filter.messages.contains(&value) {
1997 self.message_filter.is_in = true;
1998 return false;
1999 }
2000 }
2001 }
2002 }
2003 }
2004 Event::End(bytes) => {
2005 let Some(id) = identify_element(bytes.name().into_inner()) else {
2006 panic!(
2007 "unexpected element {:?}",
2008 String::from_utf8_lossy(bytes.name().into_inner())
2009 );
2010 };
2011
2012 if id == MavXmlElement::Message && self.message_filter.is_in {
2013 self.message_filter.is_in = false;
2014 return false;
2015 }
2016 }
2017 _ => {}
2018 }
2019 !self.message_filter.is_in
2020 }
2021 Err(error) => panic!("Failed to filter XML: {error}"),
2022 }
2023 }
2024}
2025
2026#[inline(always)]
2027fn to_pascal_case(text: impl AsRef<[u8]>) -> String {
2028 let input = text.as_ref();
2029 let mut result = String::with_capacity(input.len());
2030 let mut capitalize = true;
2031
2032 for &b in input {
2033 if b == b'_' {
2034 capitalize = true;
2035 continue;
2036 }
2037
2038 if capitalize {
2039 result.push((b as char).to_ascii_uppercase());
2040 capitalize = false;
2041 } else {
2042 result.push((b as char).to_ascii_lowercase());
2043 }
2044 }
2045
2046 result
2047}
2048
2049#[cfg(test)]
2050mod tests {
2051 use super::*;
2052
2053 #[test]
2054 fn emits_target_id_match_arms() {
2055 let mut profile = MavProfile::default();
2057
2058 let msg_with_targets = MavMessage {
2059 id: 300,
2060 name: "COMMAND_INT".to_string(),
2061 description: None,
2062 fields: vec![
2063 MavField {
2064 mavtype: MavType::UInt8,
2065 name: "target_system".to_string(),
2066 description: None,
2067 enumtype: None,
2068 display: None,
2069 is_extension: false,
2070 },
2071 MavField {
2072 mavtype: MavType::UInt8,
2073 name: "target_component".to_string(),
2074 description: None,
2075 enumtype: None,
2076 display: None,
2077 is_extension: false,
2078 },
2079 ],
2080 deprecated: None,
2081 };
2082
2083 let msg_without_targets = MavMessage {
2084 id: 0,
2085 name: "HEARTBEAT".to_string(),
2086 description: None,
2087 fields: vec![MavField {
2088 mavtype: MavType::UInt32,
2089 name: "custom_mode".to_string(),
2090 description: None,
2091 enumtype: None,
2092 display: None,
2093 is_extension: false,
2094 }],
2095 deprecated: None,
2096 };
2097
2098 profile.add_message(&msg_with_targets);
2099 profile.add_message(&msg_without_targets);
2100
2101 let tokens = profile.emit_rust("common");
2102 let mut code = tokens.to_string();
2103 code.retain(|c| !c.is_whitespace());
2104
2105 assert!(code.contains("fntarget_system_id(&self)->Option<u8>"));
2107 assert!(code.contains("fntarget_component_id(&self)->Option<u8>"));
2108
2109 assert!(code.contains("Self::COMMAND_INT(inner)=>Some(inner.target_system)"));
2111 assert!(code.contains("Self::COMMAND_INT(inner)=>Some(inner.target_component)"));
2112
2113 assert!(!code.contains("Self::HEARTBEAT(inner)=>Some(inner.target_system)"));
2115 assert!(!code.contains("Self::HEARTBEAT(inner)=>Some(inner.target_component)"));
2116 }
2117
2118 #[test]
2119 fn validate_unique_fields_allows_unique() {
2120 let msg = MavMessage {
2121 id: 1,
2122 name: "FOO".to_string(),
2123 description: None,
2124 fields: vec![
2125 MavField {
2126 mavtype: MavType::UInt8,
2127 name: "a".to_string(),
2128 description: None,
2129 enumtype: None,
2130 display: None,
2131 is_extension: false,
2132 },
2133 MavField {
2134 mavtype: MavType::UInt16,
2135 name: "b".to_string(),
2136 description: None,
2137 enumtype: None,
2138 display: None,
2139 is_extension: false,
2140 },
2141 ],
2142 deprecated: None,
2143 };
2144 msg.validate_unique_fields();
2146 }
2147
2148 #[test]
2149 #[should_panic(expected = "Duplicate field")]
2150 fn validate_unique_fields_panics_on_duplicate() {
2151 let msg = MavMessage {
2152 id: 2,
2153 name: "BAR".to_string(),
2154 description: None,
2155 fields: vec![
2156 MavField {
2157 mavtype: MavType::UInt8,
2158 name: "target_system".to_string(),
2159 description: None,
2160 enumtype: None,
2161 display: None,
2162 is_extension: false,
2163 },
2164 MavField {
2165 mavtype: MavType::UInt8,
2166 name: "target_system".to_string(),
2167 description: None,
2168 enumtype: None,
2169 display: None,
2170 is_extension: false,
2171 },
2172 ],
2173 deprecated: None,
2174 };
2175 msg.validate_unique_fields();
2177 }
2178
2179 #[test]
2180 fn validate_field_count_ok() {
2181 let msg = MavMessage {
2182 id: 2,
2183 name: "FOO".to_string(),
2184 description: None,
2185 fields: vec![
2186 MavField {
2187 mavtype: MavType::UInt8,
2188 name: "a".to_string(),
2189 description: None,
2190 enumtype: None,
2191 display: None,
2192 is_extension: false,
2193 },
2194 MavField {
2195 mavtype: MavType::UInt8,
2196 name: "b".to_string(),
2197 description: None,
2198 enumtype: None,
2199 display: None,
2200 is_extension: false,
2201 },
2202 ],
2203 deprecated: None,
2204 };
2205 msg.validate_field_count();
2207 }
2208
2209 #[test]
2210 #[should_panic]
2211 fn validate_field_count_too_many() {
2212 let mut fields = vec![];
2213 for i in 0..65 {
2214 let field = MavField {
2215 mavtype: MavType::UInt8,
2216 name: format!("field_{i}"),
2217 description: None,
2218 enumtype: None,
2219 display: None,
2220 is_extension: false,
2221 };
2222 fields.push(field);
2223 }
2224 let msg = MavMessage {
2225 id: 2,
2226 name: "BAZ".to_string(),
2227 description: None,
2228 fields,
2229 deprecated: None,
2230 };
2231 msg.validate_field_count();
2233 }
2234
2235 #[test]
2236 #[should_panic]
2237 fn validate_field_count_empty() {
2238 let msg = MavMessage {
2239 id: 2,
2240 name: "BAM".to_string(),
2241 description: None,
2242 fields: vec![],
2243 deprecated: None,
2244 };
2245 msg.validate_field_count();
2247 }
2248}