mavlink_bindgen/
parser.rs

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