mavlink_bindgen/
parser.rs

1use crc_any::CRCu16;
2use std::cmp::Ordering;
3use std::collections::hash_map::Entry;
4use std::collections::{HashMap, 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 quick_xml::{events::Event, Reader};
12
13use proc_macro2::{Ident, TokenStream};
14use quote::{format_ident, quote};
15
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19use crate::error::BindGenError;
20
21#[derive(Debug, PartialEq, Clone, Default)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct MavProfile {
24    pub messages: HashMap<String, MavMessage>,
25    pub enums: HashMap<String, MavEnum>,
26}
27
28impl MavProfile {
29    fn add_message(&mut self, message: &MavMessage) {
30        match self.messages.entry(message.name.clone()) {
31            Entry::Occupied(entry) => {
32                assert!(
33                    entry.get() == message,
34                    "Message '{}' defined twice but definitions are different",
35                    message.name
36                );
37            }
38            Entry::Vacant(entry) => {
39                entry.insert(message.clone());
40            }
41        }
42    }
43
44    fn add_enum(&mut self, enm: &MavEnum) {
45        match self.enums.entry(enm.name.clone()) {
46            Entry::Occupied(entry) => {
47                entry.into_mut().try_combine(enm);
48            }
49            Entry::Vacant(entry) => {
50                entry.insert(enm.clone());
51            }
52        }
53    }
54
55    /// Go over all fields in the messages, and if you encounter an enum,
56    /// which is a bitmask, set the bitmask size based on field size
57    fn update_enums(mut self) -> Self {
58        for msg in self.messages.values_mut() {
59            for field in &mut msg.fields {
60                if let Some(enum_name) = &field.enumtype {
61                    // find the corresponding enum
62                    if let Some(enm) = self.enums.get_mut(enum_name) {
63                        // Handle legacy definition where bitmask is defined as display="bitmask"
64                        if field.display == Some("bitmask".to_string()) {
65                            enm.bitmask = true;
66                        }
67
68                        // it is a bitmask
69                        if enm.bitmask {
70                            enm.primitive = Some(field.mavtype.rust_primitive_type());
71
72                            // Fix fields in backwards manner
73                            if field.display.is_none() {
74                                field.display = Some("bitmask".to_string());
75                            }
76                        }
77                    }
78                }
79            }
80        }
81        self
82    }
83
84    //TODO verify this is no longer necessary since we're supporting both mavlink1 and mavlink2
85    //    ///If we are not using Mavlink v2, remove messages with id's > 254
86    //    fn update_messages(mut self) -> Self {
87    //        //println!("Updating messages");
88    //        let msgs = self.messages.into_iter().filter(
89    //            |x| x.id <= 254).collect::<Vec<MavMessage>>();
90    //        self.messages = msgs;
91    //        self
92    //    }
93
94    /// Simple header comment
95    fn emit_comments(&self) -> TokenStream {
96        quote!(#![doc = "This file was automatically generated, do not edit"])
97    }
98
99    /// Emit rust messages
100    fn emit_msgs(&self) -> Vec<TokenStream> {
101        self.messages.values().map(|d| d.emit_rust()).collect()
102    }
103
104    /// Emit rust enums
105    fn emit_enums(&self) -> Vec<TokenStream> {
106        self.enums.values().map(|d| d.emit_rust()).collect()
107    }
108
109    /// Get list of original message names
110    fn emit_enum_names(&self) -> Vec<TokenStream> {
111        self.messages
112            .values()
113            .map(|msg| {
114                let name = format_ident!("{}", msg.name);
115                quote!(#name)
116            })
117            .collect()
118    }
119
120    /// Emit message names with "_DATA" at the end
121    fn emit_struct_names(&self) -> Vec<TokenStream> {
122        self.messages
123            .values()
124            .map(|msg| msg.emit_struct_name())
125            .collect()
126    }
127
128    fn emit_rust(&self) -> TokenStream {
129        //TODO verify that id_width of u8 is OK even in mavlink v1
130        let id_width = format_ident!("u32");
131
132        let comment = self.emit_comments();
133        let msgs = self.emit_msgs();
134        let enum_names = self.emit_enum_names();
135        let struct_names = self.emit_struct_names();
136        let enums = self.emit_enums();
137
138        let mav_message = self.emit_mav_message(&enum_names, &struct_names);
139        let mav_message_parse = self.emit_mav_message_parse(&enum_names, &struct_names);
140        let mav_message_crc = self.emit_mav_message_crc(&id_width, &struct_names);
141        let mav_message_name = self.emit_mav_message_name(&enum_names, &struct_names);
142        let mav_message_id = self.emit_mav_message_id(&enum_names, &struct_names);
143        let mav_message_id_from_name = self.emit_mav_message_id_from_name(&struct_names);
144        let mav_message_default_from_id =
145            self.emit_mav_message_default_from_id(&enum_names, &struct_names);
146        let mav_message_random_from_id =
147            self.emit_mav_message_random_from_id(&enum_names, &struct_names);
148        let mav_message_serialize = self.emit_mav_message_serialize(&enum_names);
149
150        quote! {
151            #comment
152            #[allow(unused_imports)]
153            use num_derive::FromPrimitive;
154            #[allow(unused_imports)]
155            use num_traits::FromPrimitive;
156            #[allow(unused_imports)]
157            use num_derive::ToPrimitive;
158            #[allow(unused_imports)]
159            use num_traits::ToPrimitive;
160            #[allow(unused_imports)]
161            use bitflags::bitflags;
162
163            use mavlink_core::{MavlinkVersion, Message, MessageData, bytes::Bytes, bytes_mut::BytesMut};
164
165            #[cfg(feature = "serde")]
166            use serde::{Serialize, Deserialize};
167
168            #[cfg(feature = "arbitrary")]
169            use arbitrary::Arbitrary;
170
171            #(#enums)*
172
173            #(#msgs)*
174
175            #[derive(Clone, PartialEq, Debug)]
176            #mav_message
177
178            impl Message for MavMessage {
179                #mav_message_parse
180                #mav_message_name
181                #mav_message_id
182                #mav_message_id_from_name
183                #mav_message_default_from_id
184                #mav_message_random_from_id
185                #mav_message_serialize
186                #mav_message_crc
187            }
188        }
189    }
190
191    fn emit_mav_message(&self, enums: &[TokenStream], structs: &[TokenStream]) -> TokenStream {
192        quote! {
193            #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
194            #[cfg_attr(feature = "serde", serde(tag = "type"))]
195            #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
196            #[repr(u32)]
197            pub enum MavMessage {
198                #(#enums(#structs),)*
199            }
200        }
201    }
202
203    fn emit_mav_message_parse(
204        &self,
205        enums: &[TokenStream],
206        structs: &[TokenStream],
207    ) -> TokenStream {
208        let id_width = format_ident!("u32");
209
210        quote! {
211            fn parse(version: MavlinkVersion, id: #id_width, payload: &[u8]) -> Result<Self, ::mavlink_core::error::ParserError> {
212                match id {
213                    #(#structs::ID => #structs::deser(version, payload).map(Self::#enums),)*
214                    _ => {
215                        Err(::mavlink_core::error::ParserError::UnknownMessage { id })
216                    },
217                }
218            }
219        }
220    }
221
222    fn emit_mav_message_crc(&self, id_width: &Ident, structs: &[TokenStream]) -> TokenStream {
223        quote! {
224            fn extra_crc(id: #id_width) -> u8 {
225                match id {
226                    #(#structs::ID => #structs::EXTRA_CRC,)*
227                    _ => {
228                        0
229                    },
230                }
231            }
232        }
233    }
234
235    fn emit_mav_message_name(&self, enums: &[TokenStream], structs: &[TokenStream]) -> TokenStream {
236        quote! {
237            fn message_name(&self) -> &'static str {
238                match self {
239                    #(Self::#enums(..) => #structs::NAME,)*
240                }
241            }
242        }
243    }
244
245    fn emit_mav_message_id(&self, enums: &[TokenStream], structs: &[TokenStream]) -> TokenStream {
246        let id_width = format_ident!("u32");
247        quote! {
248            fn message_id(&self) -> #id_width {
249                match self {
250                    #(Self::#enums(..) => #structs::ID,)*
251                }
252            }
253        }
254    }
255
256    fn emit_mav_message_id_from_name(&self, structs: &[TokenStream]) -> TokenStream {
257        quote! {
258            fn message_id_from_name(name: &str) -> Result<u32, &'static str> {
259                match name {
260                    #(#structs::NAME => Ok(#structs::ID),)*
261                    _ => {
262                        Err("Invalid message name.")
263                    }
264                }
265            }
266        }
267    }
268
269    fn emit_mav_message_default_from_id(
270        &self,
271        enums: &[TokenStream],
272        structs: &[TokenStream],
273    ) -> TokenStream {
274        quote! {
275            fn default_message_from_id(id: u32) -> Result<Self, &'static str> {
276                match id {
277                    #(#structs::ID => Ok(Self::#enums(#structs::default())),)*
278                    _ => {
279                        Err("Invalid message id.")
280                    }
281                }
282            }
283        }
284    }
285
286    fn emit_mav_message_random_from_id(
287        &self,
288        enums: &[TokenStream],
289        structs: &[TokenStream],
290    ) -> TokenStream {
291        quote! {
292            #[cfg(feature = "arbitrary")]
293            fn random_message_from_id<R: rand::RngCore>(id: u32, rng: &mut R) -> Result<Self, &'static str> {
294                match id {
295                    #(#structs::ID => Ok(Self::#enums(#structs::random(rng))),)*
296                    _ => Err("Invalid message id."),
297                }
298            }
299        }
300    }
301
302    fn emit_mav_message_serialize(&self, enums: &Vec<TokenStream>) -> TokenStream {
303        quote! {
304            fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize {
305                match self {
306                    #(Self::#enums(body) => body.ser(version, bytes),)*
307                }
308            }
309        }
310    }
311}
312
313#[derive(Debug, PartialEq, Eq, Clone, Default)]
314#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
315pub struct MavEnum {
316    pub name: String,
317    pub description: Option<String>,
318    pub entries: Vec<MavEnumEntry>,
319    /// If contains Some, the string represents the primitive type (size) for bitflags.
320    /// If no fields use this enum, the bitmask is true, but primitive is None. In this case
321    /// regular enum is generated as primitive is unknown.
322    pub primitive: Option<String>,
323    pub bitmask: bool,
324}
325
326impl MavEnum {
327    fn try_combine(&mut self, enm: &Self) {
328        if self.name == enm.name {
329            for enum_entry in &enm.entries {
330                let found_entry = self.entries.iter().find(|elem| {
331                    elem.name == enum_entry.name && elem.value.unwrap() == enum_entry.value.unwrap()
332                });
333                match found_entry {
334                    Some(entry) => panic!("Enum entry {} already exists", entry.name),
335                    None => self.entries.push(enum_entry.clone()),
336                }
337            }
338        }
339    }
340
341    fn emit_defs(&self) -> Vec<TokenStream> {
342        let mut cnt = 0u32;
343        self.entries
344            .iter()
345            .map(|enum_entry| {
346                let name = format_ident!("{}", enum_entry.name.clone());
347                let value;
348
349                #[cfg(feature = "emit-description")]
350                let description = if let Some(description) = enum_entry.description.as_ref() {
351                    quote!(#[doc = #description])
352                } else {
353                    quote!()
354                };
355
356                #[cfg(not(feature = "emit-description"))]
357                let description = quote!();
358
359                if enum_entry.value.is_none() {
360                    cnt += 1;
361                    value = quote!(#cnt);
362                } else {
363                    let tmp_value = enum_entry.value.unwrap();
364                    cnt = cnt.max(tmp_value);
365                    let tmp = TokenStream::from_str(&tmp_value.to_string()).unwrap();
366                    value = quote!(#tmp);
367                };
368                if self.primitive.is_some() {
369                    quote! {
370                        #description
371                        const #name = #value;
372                    }
373                } else {
374                    quote! {
375                        #description
376                        #name = #value,
377                    }
378                }
379            })
380            .collect()
381    }
382
383    fn emit_name(&self) -> TokenStream {
384        let name = format_ident!("{}", self.name);
385        quote!(#name)
386    }
387
388    fn emit_const_default(&self) -> TokenStream {
389        let default = format_ident!("{}", self.entries[0].name);
390        quote!(pub const DEFAULT: Self = Self::#default;)
391    }
392
393    fn emit_rust(&self) -> TokenStream {
394        let defs = self.emit_defs();
395        let enum_name = self.emit_name();
396        let const_default = self.emit_const_default();
397
398        #[cfg(feature = "emit-description")]
399        let description = if let Some(description) = self.description.as_ref() {
400            let desc = description.to_string();
401            quote!(#[doc = #desc])
402        } else {
403            quote!()
404        };
405
406        #[cfg(not(feature = "emit-description"))]
407        let description = quote!();
408
409        let enum_def;
410        if let Some(primitive) = self.primitive.clone() {
411            let primitive = format_ident!("{}", primitive);
412            enum_def = quote! {
413                bitflags!{
414                    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
415                    #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
416                    #description
417                    pub struct #enum_name: #primitive {
418                        #(#defs)*
419                    }
420                }
421            };
422        } else {
423            enum_def = quote! {
424                #[derive(Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)]
425                #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
426                #[cfg_attr(feature = "serde", serde(tag = "type"))]
427                #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
428                #[repr(u32)]
429                #description
430                pub enum #enum_name {
431                    #(#defs)*
432                }
433            };
434        }
435
436        quote! {
437            #enum_def
438
439            impl #enum_name {
440                #const_default
441            }
442
443            impl Default for #enum_name {
444                fn default() -> Self {
445                    Self::DEFAULT
446                }
447            }
448        }
449    }
450}
451
452#[derive(Debug, PartialEq, Eq, Clone, Default)]
453#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
454pub struct MavEnumEntry {
455    pub value: Option<u32>,
456    pub name: String,
457    pub description: Option<String>,
458    pub params: Option<Vec<String>>,
459}
460
461#[derive(Debug, PartialEq, Clone, Default)]
462#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
463pub struct MavMessage {
464    pub id: u32,
465    pub name: String,
466    pub description: Option<String>,
467    pub fields: Vec<MavField>,
468}
469
470impl MavMessage {
471    /// Return Token of "MESSAGE_NAME_DATA
472    /// for mavlink struct data
473    fn emit_struct_name(&self) -> TokenStream {
474        let name = format_ident!("{}", format!("{}_DATA", self.name));
475        quote!(#name)
476    }
477
478    fn emit_name_types(&self) -> (Vec<TokenStream>, usize) {
479        let mut encoded_payload_len: usize = 0;
480        let field_toks = self
481            .fields
482            .iter()
483            .map(|field| {
484                let nametype = field.emit_name_type();
485                encoded_payload_len += field.mavtype.len();
486
487                #[cfg(feature = "emit-description")]
488                let description = field.emit_description();
489
490                #[cfg(not(feature = "emit-description"))]
491                let description = quote!();
492
493                // From MAVLink specification:
494                // If sent by an implementation that doesn't have the extensions fields
495                // then the recipient will see zero values for the extensions fields.
496                let serde_default = if field.is_extension {
497                    if field.enumtype.is_some() {
498                        quote!(#[cfg_attr(feature = "serde", serde(default))])
499                    } else {
500                        quote!(#[cfg_attr(feature = "serde", serde(default = "crate::RustDefault::rust_default"))])
501                    }
502                } else {
503                    quote!()
504                };
505
506                let serde_with_attr = if matches!(field.mavtype, MavType::Array(_, _)) {
507                    quote!(#[cfg_attr(feature = "serde", serde(with = "serde_arrays"))])
508                } else {
509                    quote!()
510                };
511
512                quote! {
513                    #description
514                    #serde_default
515                    #serde_with_attr
516                    #nametype
517                }
518            })
519            .collect::<Vec<TokenStream>>();
520        (field_toks, encoded_payload_len)
521    }
522
523    /// Generate description for the given message
524    #[cfg(feature = "emit-description")]
525    fn emit_description(&self) -> TokenStream {
526        let mut ts = TokenStream::new();
527        let desc = format!("id: {}", self.id);
528        ts.extend(quote!(#[doc = #desc]));
529        if let Some(val) = self.description.clone() {
530            let doc = &format!("{val}.");
531            ts.extend(quote!(#[doc = #doc]));
532        }
533        ts
534    }
535
536    fn emit_serialize_vars(&self) -> TokenStream {
537        let ser_vars = self.fields.iter().map(|f| f.rust_writer());
538
539        quote! {
540            let mut __tmp = BytesMut::new(bytes);
541
542            // TODO: these lints are produced on a couple of cubepilot messages
543            // because they are generated as empty structs with no fields and
544            // therefore Self::ENCODED_LEN is 0. This itself is a bug because
545            // cubepilot.xml has unclosed tags in fields, which the parser has
546            // bad time handling. It should probably be fixed in both the parser
547            // and mavlink message definitions. However, until it's done, let's
548            // skip the lints.
549            #[allow(clippy::absurd_extreme_comparisons)]
550            #[allow(unused_comparisons)]
551            if __tmp.remaining() < Self::ENCODED_LEN {
552                panic!(
553                    "buffer is too small (need {} bytes, but got {})",
554                    Self::ENCODED_LEN,
555                    __tmp.remaining(),
556                )
557            }
558
559            #(#ser_vars)*
560            if matches!(version, MavlinkVersion::V2) {
561                let len = __tmp.len();
562                ::mavlink_core::utils::remove_trailing_zeroes(&bytes[..len])
563            } else {
564                __tmp.len()
565            }
566        }
567    }
568
569    fn emit_deserialize_vars(&self) -> TokenStream {
570        let deser_vars = self
571            .fields
572            .iter()
573            .map(|f| f.rust_reader())
574            .collect::<Vec<TokenStream>>();
575
576        if deser_vars.is_empty() {
577            // struct has no fields
578            quote! {
579                Ok(Self::default())
580            }
581        } else {
582            quote! {
583                let avail_len = __input.len();
584
585                let mut payload_buf  = [0; Self::ENCODED_LEN];
586                let mut buf = if avail_len < Self::ENCODED_LEN {
587                    //copy available bytes into an oversized buffer filled with zeros
588                    payload_buf[0..avail_len].copy_from_slice(__input);
589                    Bytes::new(&payload_buf)
590                } else {
591                    // fast zero copy
592                    Bytes::new(__input)
593                };
594
595                let mut __struct = Self::default();
596                #(#deser_vars)*
597                Ok(__struct)
598            }
599        }
600    }
601
602    fn emit_default_impl(&self) -> TokenStream {
603        let msg_name = self.emit_struct_name();
604        quote! {
605            impl Default for #msg_name {
606                fn default() -> Self {
607                    Self::DEFAULT.clone()
608                }
609            }
610        }
611    }
612
613    fn emit_const_default(&self) -> TokenStream {
614        let initializers = self
615            .fields
616            .iter()
617            .map(|field| field.emit_default_initializer());
618        quote!(pub const DEFAULT: Self = Self { #(#initializers)* };)
619    }
620
621    fn emit_rust(&self) -> TokenStream {
622        let msg_name = self.emit_struct_name();
623        let id = self.id;
624        let name = self.name.clone();
625        let extra_crc = extra_crc(self);
626        let (name_types, msg_encoded_len) = self.emit_name_types();
627
628        let deser_vars = self.emit_deserialize_vars();
629        let serialize_vars = self.emit_serialize_vars();
630        let const_default = self.emit_const_default();
631        let default_impl = self.emit_default_impl();
632
633        #[cfg(feature = "emit-description")]
634        let description = self.emit_description();
635
636        #[cfg(not(feature = "emit-description"))]
637        let description = quote!();
638
639        quote! {
640            #description
641            #[derive(Debug, Clone, PartialEq)]
642            #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
643            #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
644            pub struct #msg_name {
645                #(#name_types)*
646            }
647
648            impl #msg_name {
649                pub const ENCODED_LEN: usize = #msg_encoded_len;
650                #const_default
651
652                #[cfg(feature = "arbitrary")]
653                pub fn random<R: rand::RngCore>(rng: &mut R) -> Self {
654                    use arbitrary::{Unstructured, Arbitrary};
655                    let mut buf = [0u8; 1024];
656                    rng.fill_bytes(&mut buf);
657                    let mut unstructured = Unstructured::new(&buf);
658                    Self::arbitrary(&mut unstructured).unwrap_or_default()
659                }
660            }
661
662            #default_impl
663
664            impl MessageData for #msg_name {
665                type Message = MavMessage;
666
667                const ID: u32 = #id;
668                const NAME: &'static str = #name;
669                const EXTRA_CRC: u8 = #extra_crc;
670                const ENCODED_LEN: usize = #msg_encoded_len;
671
672                fn deser(_version: MavlinkVersion, __input: &[u8]) -> Result<Self, ::mavlink_core::error::ParserError> {
673                    #deser_vars
674                }
675
676                fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize {
677                    #serialize_vars
678                }
679            }
680        }
681    }
682}
683
684#[derive(Debug, PartialEq, Clone, Default)]
685#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
686pub struct MavField {
687    pub mavtype: MavType,
688    pub name: String,
689    pub description: Option<String>,
690    pub enumtype: Option<String>,
691    pub display: Option<String>,
692    pub is_extension: bool,
693}
694
695impl MavField {
696    /// Emit rust name of a given field
697    fn emit_name(&self) -> TokenStream {
698        let name = format_ident!("{}", self.name);
699        quote!(#name)
700    }
701
702    /// Emit rust type of the field
703    fn emit_type(&self) -> TokenStream {
704        let mavtype;
705        if matches!(self.mavtype, MavType::Array(_, _)) {
706            let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
707            mavtype = quote!(#rt);
708        } else if let Some(enumname) = &self.enumtype {
709            let en = TokenStream::from_str(enumname).unwrap();
710            mavtype = quote!(#en);
711        } else {
712            let rt = TokenStream::from_str(&self.mavtype.rust_type()).unwrap();
713            mavtype = quote!(#rt);
714        }
715        mavtype
716    }
717
718    /// Generate description for the given field
719    #[cfg(feature = "emit-description")]
720    fn emit_description(&self) -> TokenStream {
721        let mut ts = TokenStream::new();
722        if let Some(val) = self.description.clone() {
723            let desc = format!("{val}.");
724            ts.extend(quote!(#[doc = #desc]));
725        }
726        ts
727    }
728
729    /// Combine rust name and type of a given field
730    fn emit_name_type(&self) -> TokenStream {
731        let name = self.emit_name();
732        let fieldtype = self.emit_type();
733        quote!(pub #name: #fieldtype,)
734    }
735
736    /// Emit writer
737    fn rust_writer(&self) -> TokenStream {
738        let mut name = "self.".to_string() + &self.name.clone();
739        if self.enumtype.is_some() {
740            // casts are not necessary for arrays, because they are currently
741            // generated as primitive arrays
742            if !matches!(self.mavtype, MavType::Array(_, _)) {
743                if let Some(dsp) = &self.display {
744                    // potentially a bitflag
745                    if dsp == "bitmask" {
746                        // it is a bitflag
747                        name += ".bits()";
748                    } else {
749                        panic!("Display option not implemented");
750                    }
751                } else {
752                    // an enum, have to use "*foo as u8" cast
753                    name += " as ";
754                    name += &self.mavtype.rust_type();
755                }
756            }
757        }
758        let ts = TokenStream::from_str(&name).unwrap();
759        let name = quote!(#ts);
760        let buf = format_ident!("__tmp");
761        self.mavtype.rust_writer(&name, buf)
762    }
763
764    /// Emit reader
765    fn rust_reader(&self) -> TokenStream {
766        let _name = TokenStream::from_str(&self.name).unwrap();
767
768        let name = quote!(__struct.#_name);
769        let buf = format_ident!("buf");
770        if let Some(enum_name) = &self.enumtype {
771            // TODO: handle enum arrays properly, rather than just generating
772            // primitive arrays
773            if let MavType::Array(_t, _size) = &self.mavtype {
774                return self.mavtype.rust_reader(&name, buf);
775            }
776            if let Some(dsp) = &self.display {
777                if dsp == "bitmask" {
778                    // bitflags
779                    let tmp = self.mavtype.rust_reader(&quote!(let tmp), buf);
780                    let enum_name_ident = format_ident!("{}", enum_name);
781                    quote! {
782                        #tmp
783                        #name = #enum_name_ident::from_bits(tmp & #enum_name_ident::all().bits())
784                            .ok_or(::mavlink_core::error::ParserError::InvalidFlag { flag_type: #enum_name, value: tmp as u32 })?;
785                    }
786                } else {
787                    panic!("Display option not implemented");
788                }
789            } else {
790                // handle enum by FromPrimitive
791                let tmp = self.mavtype.rust_reader(&quote!(let tmp), buf);
792                let val = format_ident!("from_{}", &self.mavtype.rust_type());
793                quote!(
794                    #tmp
795                    #name = FromPrimitive::#val(tmp)
796                        .ok_or(::mavlink_core::error::ParserError::InvalidEnum { enum_type: #enum_name, value: tmp as u32 })?;
797                )
798            }
799        } else {
800            self.mavtype.rust_reader(&name, buf)
801        }
802    }
803
804    fn emit_default_initializer(&self) -> TokenStream {
805        let field = self.emit_name();
806        // FIXME: Is this actually expected behaviour??
807        if matches!(self.mavtype, MavType::Array(_, _)) {
808            let default_value = self.mavtype.emit_default_value();
809            quote!(#field: #default_value,)
810        } else if let Some(enumname) = &self.enumtype {
811            let ty = TokenStream::from_str(enumname).unwrap();
812            quote!(#field: #ty::DEFAULT,)
813        } else {
814            let default_value = self.mavtype.emit_default_value();
815            quote!(#field: #default_value,)
816        }
817    }
818}
819
820#[derive(Debug, PartialEq, Clone, Default)]
821#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
822pub enum MavType {
823    UInt8MavlinkVersion,
824    #[default]
825    UInt8,
826    UInt16,
827    UInt32,
828    UInt64,
829    Int8,
830    Int16,
831    Int32,
832    Int64,
833    Char,
834    Float,
835    Double,
836    Array(Box<MavType>, usize),
837}
838
839impl MavType {
840    fn parse_type(s: &str) -> Option<Self> {
841        use self::MavType::*;
842        match s {
843            "uint8_t_mavlink_version" => Some(UInt8MavlinkVersion),
844            "uint8_t" => Some(UInt8),
845            "uint16_t" => Some(UInt16),
846            "uint32_t" => Some(UInt32),
847            "uint64_t" => Some(UInt64),
848            "int8_t" => Some(Int8),
849            "int16_t" => Some(Int16),
850            "int32_t" => Some(Int32),
851            "int64_t" => Some(Int64),
852            "char" => Some(Char),
853            "float" => Some(Float),
854            "Double" => Some(Double),
855            "double" => Some(Double),
856            _ => {
857                if s.ends_with(']') {
858                    let start = s.find('[')?;
859                    let size = s[start + 1..(s.len() - 1)].parse::<usize>().ok()?;
860                    let mtype = Self::parse_type(&s[0..start])?;
861                    Some(Array(Box::new(mtype), size))
862                } else {
863                    None
864                }
865            }
866        }
867    }
868
869    /// Emit reader of a given type
870    pub fn rust_reader(&self, val: &TokenStream, buf: Ident) -> TokenStream {
871        use self::MavType::*;
872        match self {
873            Char => quote! {#val = #buf.get_u8();},
874            UInt8 => quote! {#val = #buf.get_u8();},
875            UInt16 => quote! {#val = #buf.get_u16_le();},
876            UInt32 => quote! {#val = #buf.get_u32_le();},
877            UInt64 => quote! {#val = #buf.get_u64_le();},
878            UInt8MavlinkVersion => quote! {#val = #buf.get_u8();},
879            Int8 => quote! {#val = #buf.get_i8();},
880            Int16 => quote! {#val = #buf.get_i16_le();},
881            Int32 => quote! {#val = #buf.get_i32_le();},
882            Int64 => quote! {#val = #buf.get_i64_le();},
883            Float => quote! {#val = #buf.get_f32_le();},
884            Double => quote! {#val = #buf.get_f64_le();},
885            Array(t, _) => {
886                let r = t.rust_reader(&quote!(let val), buf);
887                quote! {
888                    for v in &mut #val {
889                        #r
890                        *v = val;
891                    }
892                }
893            }
894        }
895    }
896
897    /// Emit writer of a given type
898    pub fn rust_writer(&self, val: &TokenStream, buf: Ident) -> TokenStream {
899        use self::MavType::*;
900        match self {
901            UInt8MavlinkVersion => quote! {#buf.put_u8(#val);},
902            UInt8 => quote! {#buf.put_u8(#val);},
903            Char => quote! {#buf.put_u8(#val);},
904            UInt16 => quote! {#buf.put_u16_le(#val);},
905            UInt32 => quote! {#buf.put_u32_le(#val);},
906            Int8 => quote! {#buf.put_i8(#val);},
907            Int16 => quote! {#buf.put_i16_le(#val);},
908            Int32 => quote! {#buf.put_i32_le(#val);},
909            Float => quote! {#buf.put_f32_le(#val);},
910            UInt64 => quote! {#buf.put_u64_le(#val);},
911            Int64 => quote! {#buf.put_i64_le(#val);},
912            Double => quote! {#buf.put_f64_le(#val);},
913            Array(t, _size) => {
914                let w = t.rust_writer(&quote!(*val), buf);
915                quote! {
916                    for val in &#val {
917                        #w
918                    }
919                }
920            }
921        }
922    }
923
924    /// Size of a given Mavtype
925    fn len(&self) -> usize {
926        use self::MavType::*;
927        match self {
928            UInt8MavlinkVersion | UInt8 | Int8 | Char => 1,
929            UInt16 | Int16 => 2,
930            UInt32 | Int32 | Float => 4,
931            UInt64 | Int64 | Double => 8,
932            Array(t, size) => t.len() * size,
933        }
934    }
935
936    /// Used for ordering of types
937    fn order_len(&self) -> usize {
938        use self::MavType::*;
939        match self {
940            UInt8MavlinkVersion | UInt8 | Int8 | Char => 1,
941            UInt16 | Int16 => 2,
942            UInt32 | Int32 | Float => 4,
943            UInt64 | Int64 | Double => 8,
944            Array(t, _) => t.len(),
945        }
946    }
947
948    /// Used for crc calculation
949    pub fn primitive_type(&self) -> String {
950        use self::MavType::*;
951        match self {
952            UInt8MavlinkVersion => "uint8_t".into(),
953            UInt8 => "uint8_t".into(),
954            Int8 => "int8_t".into(),
955            Char => "char".into(),
956            UInt16 => "uint16_t".into(),
957            Int16 => "int16_t".into(),
958            UInt32 => "uint32_t".into(),
959            Int32 => "int32_t".into(),
960            Float => "float".into(),
961            UInt64 => "uint64_t".into(),
962            Int64 => "int64_t".into(),
963            Double => "double".into(),
964            Array(t, _) => t.primitive_type(),
965        }
966    }
967
968    /// Return rust equivalent of a given Mavtype
969    /// Used for generating struct fields.
970    pub fn rust_type(&self) -> String {
971        use self::MavType::*;
972        match self {
973            UInt8 | UInt8MavlinkVersion => "u8".into(),
974            Int8 => "i8".into(),
975            Char => "u8".into(),
976            UInt16 => "u16".into(),
977            Int16 => "i16".into(),
978            UInt32 => "u32".into(),
979            Int32 => "i32".into(),
980            Float => "f32".into(),
981            UInt64 => "u64".into(),
982            Int64 => "i64".into(),
983            Double => "f64".into(),
984            Array(t, size) => format!("[{};{}]", t.rust_type(), size),
985        }
986    }
987
988    pub fn emit_default_value(&self) -> TokenStream {
989        use self::MavType::*;
990        match self {
991            UInt8 | UInt8MavlinkVersion => quote!(0_u8),
992            Int8 => quote!(0_i8),
993            Char => quote!(0_u8),
994            UInt16 => quote!(0_u16),
995            Int16 => quote!(0_i16),
996            UInt32 => quote!(0_u32),
997            Int32 => quote!(0_i32),
998            Float => quote!(0.0_f32),
999            UInt64 => quote!(0_u64),
1000            Int64 => quote!(0_i64),
1001            Double => quote!(0.0_f64),
1002            Array(ty, size) => {
1003                let default_value = ty.emit_default_value();
1004                quote!([#default_value; #size])
1005            }
1006        }
1007    }
1008
1009    /// Return rust equivalent of the primitive type of a MavType. The primitive
1010    /// type is the type itself for all except arrays, in which case it is the
1011    /// element type.
1012    pub fn rust_primitive_type(&self) -> String {
1013        use self::MavType::*;
1014        match self {
1015            Array(t, _) => t.rust_primitive_type(),
1016            _ => self.rust_type(),
1017        }
1018    }
1019
1020    /// Compare two MavTypes
1021    pub fn compare(&self, other: &Self) -> Ordering {
1022        let len = self.order_len();
1023        (-(len as isize)).cmp(&(-(other.order_len() as isize)))
1024    }
1025}
1026
1027#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1028#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1029#[cfg_attr(feature = "serde", serde(tag = "type"))]
1030pub enum MavXmlElement {
1031    Version,
1032    Mavlink,
1033    Dialect,
1034    Include,
1035    Enums,
1036    Enum,
1037    Entry,
1038    Description,
1039    Param,
1040    Messages,
1041    Message,
1042    Field,
1043    Deprecated,
1044    Wip,
1045    Extensions,
1046}
1047
1048const fn identify_element(s: &[u8]) -> Option<MavXmlElement> {
1049    use self::MavXmlElement::*;
1050    match s {
1051        b"version" => Some(Version),
1052        b"mavlink" => Some(Mavlink),
1053        b"dialect" => Some(Dialect),
1054        b"include" => Some(Include),
1055        b"enums" => Some(Enums),
1056        b"enum" => Some(Enum),
1057        b"entry" => Some(Entry),
1058        b"description" => Some(Description),
1059        b"param" => Some(Param),
1060        b"messages" => Some(Messages),
1061        b"message" => Some(Message),
1062        b"field" => Some(Field),
1063        b"deprecated" => Some(Deprecated),
1064        b"wip" => Some(Wip),
1065        b"extensions" => Some(Extensions),
1066        _ => None,
1067    }
1068}
1069
1070fn is_valid_parent(p: Option<MavXmlElement>, s: MavXmlElement) -> bool {
1071    use self::MavXmlElement::*;
1072    match s {
1073        Version => p == Some(Mavlink),
1074        Mavlink => p.is_none(),
1075        Dialect => p == Some(Mavlink),
1076        Include => p == Some(Mavlink),
1077        Enums => p == Some(Mavlink),
1078        Enum => p == Some(Enums),
1079        Entry => p == Some(Enum),
1080        Description => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1081        Param => p == Some(Entry),
1082        Messages => p == Some(Mavlink),
1083        Message => p == Some(Messages),
1084        Field => p == Some(Message),
1085        Deprecated => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1086        Wip => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
1087        Extensions => p == Some(Message),
1088    }
1089}
1090
1091pub fn parse_profile(
1092    definitions_dir: &Path,
1093    definition_file: &Path,
1094    parsed_files: &mut HashSet<PathBuf>,
1095) -> Result<MavProfile, BindGenError> {
1096    let in_path = Path::new(&definitions_dir).join(definition_file);
1097    parsed_files.insert(in_path.clone()); // Keep track of which files have been parsed
1098
1099    let mut stack: Vec<MavXmlElement> = vec![];
1100
1101    let mut profile = MavProfile::default();
1102    let mut field = MavField::default();
1103    let mut message = MavMessage::default();
1104    let mut mavenum = MavEnum::default();
1105    let mut entry = MavEnumEntry::default();
1106    let mut include = PathBuf::new();
1107    let mut paramid: Option<usize> = None;
1108
1109    let mut xml_filter = MavXmlFilter::default();
1110    let mut events: Vec<Result<Event, quick_xml::Error>> = Vec::new();
1111    let file = File::open(&in_path).map_err(|e| BindGenError::CouldNotReadDefinitionFile {
1112        source: e,
1113        path: in_path.clone(),
1114    })?;
1115    let mut reader = Reader::from_reader(BufReader::new(file));
1116    reader.config_mut().trim_text(true);
1117
1118    let mut buf = Vec::new();
1119    loop {
1120        match reader.read_event_into(&mut buf) {
1121            Ok(Event::Eof) => {
1122                events.push(Ok(Event::Eof));
1123                break;
1124            }
1125            Ok(event) => events.push(Ok(event.into_owned())),
1126            Err(why) => events.push(Err(why)),
1127        }
1128        buf.clear();
1129    }
1130    xml_filter.filter(&mut events);
1131    let mut is_in_extension = false;
1132    for e in events {
1133        match e {
1134            Ok(Event::Start(bytes)) => {
1135                let Some(id) = identify_element(bytes.name().into_inner()) else {
1136                    panic!(
1137                        "unexpected element {:?}",
1138                        String::from_utf8_lossy(bytes.name().into_inner())
1139                    );
1140                };
1141
1142                assert!(
1143                    is_valid_parent(stack.last().copied(), id),
1144                    "not valid parent {:?} of {id:?}",
1145                    stack.last(),
1146                );
1147
1148                match id {
1149                    MavXmlElement::Extensions => {
1150                        is_in_extension = true;
1151                    }
1152                    MavXmlElement::Message => {
1153                        message = MavMessage::default();
1154                    }
1155                    MavXmlElement::Field => {
1156                        field = MavField::default();
1157                        field.is_extension = is_in_extension;
1158                    }
1159                    MavXmlElement::Enum => {
1160                        mavenum = MavEnum::default();
1161                    }
1162                    MavXmlElement::Entry => {
1163                        entry = MavEnumEntry::default();
1164                    }
1165                    MavXmlElement::Include => {
1166                        include = PathBuf::default();
1167                    }
1168                    MavXmlElement::Param => {
1169                        paramid = None;
1170                    }
1171                    _ => (),
1172                }
1173
1174                stack.push(id);
1175
1176                for attr in bytes.attributes() {
1177                    let attr = attr.unwrap();
1178                    match stack.last() {
1179                        Some(&MavXmlElement::Enum) => {
1180                            if attr.key.into_inner() == b"name" {
1181                                mavenum.name = to_pascal_case(attr.value);
1182                                //mavenum.name = attr.value.clone();
1183                            } else if attr.key.into_inner() == b"bitmask" {
1184                                mavenum.bitmask = true;
1185                            }
1186                        }
1187                        Some(&MavXmlElement::Entry) => {
1188                            match attr.key.into_inner() {
1189                                b"name" => {
1190                                    entry.name = String::from_utf8_lossy(&attr.value).to_string();
1191                                }
1192                                b"value" => {
1193                                    let value = String::from_utf8_lossy(&attr.value);
1194                                    // Deal with hexadecimal numbers
1195                                    let (src, radix) = value
1196                                        .strip_prefix("0x")
1197                                        .map(|value| (value, 16))
1198                                        .unwrap_or((value.as_ref(), 10));
1199                                    entry.value = u32::from_str_radix(src, radix).ok();
1200                                }
1201                                _ => (),
1202                            }
1203                        }
1204                        Some(&MavXmlElement::Message) => {
1205                            match attr.key.into_inner() {
1206                                b"name" => {
1207                                    /*message.name = attr
1208                                    .value
1209                                    .clone()
1210                                    .split("_")
1211                                    .map(|x| x.to_lowercase())
1212                                    .map(|x| {
1213                                        let mut v: Vec<char> = x.chars().collect();
1214                                        v[0] = v[0].to_uppercase().nth(0).unwrap();
1215                                        v.into_iter().collect()
1216                                    })
1217                                    .collect::<Vec<String>>()
1218                                    .join("");
1219                                    */
1220                                    message.name = String::from_utf8_lossy(&attr.value).to_string();
1221                                }
1222                                b"id" => {
1223                                    message.id =
1224                                        String::from_utf8_lossy(&attr.value).parse().unwrap();
1225                                }
1226                                _ => (),
1227                            }
1228                        }
1229                        Some(&MavXmlElement::Field) => {
1230                            match attr.key.into_inner() {
1231                                b"name" => {
1232                                    let name = String::from_utf8_lossy(&attr.value);
1233                                    field.name = if name == "type" {
1234                                        "mavtype".to_string()
1235                                    } else {
1236                                        name.to_string()
1237                                    };
1238                                }
1239                                b"type" => {
1240                                    let r#type = String::from_utf8_lossy(&attr.value);
1241                                    field.mavtype = MavType::parse_type(&r#type).unwrap();
1242                                }
1243                                b"enum" => {
1244                                    field.enumtype = Some(to_pascal_case(&attr.value));
1245
1246                                    // Update field display if enum is a bitmask
1247                                    if let Some(e) =
1248                                        profile.enums.get(field.enumtype.as_ref().unwrap())
1249                                    {
1250                                        if e.bitmask {
1251                                            field.display = Some("bitmask".to_string());
1252                                        }
1253                                    }
1254                                }
1255                                b"display" => {
1256                                    field.display =
1257                                        Some(String::from_utf8_lossy(&attr.value).to_string());
1258                                }
1259                                _ => (),
1260                            }
1261                        }
1262                        Some(&MavXmlElement::Param) => {
1263                            if entry.params.is_none() {
1264                                entry.params = Some(vec![]);
1265                            }
1266                            if attr.key.into_inner() == b"index" {
1267                                paramid =
1268                                    Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1269                            }
1270                        }
1271                        _ => (),
1272                    }
1273                }
1274            }
1275            Ok(Event::Empty(bytes)) => match bytes.name().into_inner() {
1276                b"extensions" => {
1277                    is_in_extension = true;
1278                }
1279                b"entry" => {
1280                    entry = MavEnumEntry::default();
1281                    for attr in bytes.attributes() {
1282                        let attr = attr.unwrap();
1283                        match attr.key.into_inner() {
1284                            b"name" => {
1285                                entry.name = String::from_utf8_lossy(&attr.value).to_string();
1286                            }
1287                            b"value" => {
1288                                entry.value =
1289                                    Some(String::from_utf8_lossy(&attr.value).parse().unwrap());
1290                            }
1291                            _ => (),
1292                        }
1293                    }
1294                    mavenum.entries.push(entry.clone());
1295                }
1296                _ => (),
1297            },
1298            Ok(Event::Text(bytes)) => {
1299                let s = String::from_utf8_lossy(&bytes).to_string();
1300
1301                use self::MavXmlElement::*;
1302                match (stack.last(), stack.get(stack.len() - 2)) {
1303                    (Some(&Description), Some(&Message)) => {
1304                        message.description = Some(s.replace('\n', " "));
1305                    }
1306                    (Some(&Field), Some(&Message)) => {
1307                        field.description = Some(s.replace('\n', " "));
1308                    }
1309                    (Some(&Description), Some(&Enum)) => {
1310                        mavenum.description = Some(s.replace('\n', " "));
1311                    }
1312                    (Some(&Description), Some(&Entry)) => {
1313                        entry.description = Some(s.replace('\n', " "));
1314                    }
1315                    (Some(&Param), Some(&Entry)) => {
1316                        if let Some(params) = entry.params.as_mut() {
1317                            // Some messages can jump between values, like:
1318                            // 0, 1, 2, 7
1319                            let paramid = paramid.unwrap();
1320                            if params.len() < paramid {
1321                                for index in params.len()..paramid {
1322                                    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)."));
1323                                }
1324                            }
1325                            params[paramid - 1] = s;
1326                        }
1327                    }
1328                    (Some(&Include), Some(&Mavlink)) => {
1329                        include = PathBuf::from(s.replace('\n', ""));
1330                    }
1331                    (Some(&Version), Some(&Mavlink)) => {
1332                        eprintln!("TODO: version {s:?}");
1333                    }
1334                    (Some(&Dialect), Some(&Mavlink)) => {
1335                        eprintln!("TODO: dialect {s:?}");
1336                    }
1337                    (Some(Deprecated), _) => {
1338                        eprintln!("TODO: deprecated {s:?}");
1339                    }
1340                    data => {
1341                        panic!("unexpected text data {data:?} reading {s:?}");
1342                    }
1343                }
1344            }
1345            Ok(Event::End(_)) => {
1346                match stack.last() {
1347                    Some(&MavXmlElement::Field) => message.fields.push(field.clone()),
1348                    Some(&MavXmlElement::Entry) => {
1349                        mavenum.entries.push(entry.clone());
1350                    }
1351                    Some(&MavXmlElement::Message) => {
1352                        is_in_extension = false;
1353                        // Follow mavlink ordering specification: https://mavlink.io/en/guide/serialization.html#field_reordering
1354                        let mut not_extension_fields = message.fields.clone();
1355                        let mut extension_fields = message.fields.clone();
1356
1357                        not_extension_fields.retain(|field| !field.is_extension);
1358                        extension_fields.retain(|field| field.is_extension);
1359
1360                        // Only not mavlink 1 fields need to be sorted
1361                        not_extension_fields.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1362
1363                        // Update msg fields and add the new message
1364                        let mut msg = message.clone();
1365                        msg.fields.clear();
1366                        msg.fields.extend(not_extension_fields);
1367                        msg.fields.extend(extension_fields);
1368
1369                        profile.add_message(&msg);
1370                    }
1371                    Some(&MavXmlElement::Enum) => {
1372                        profile.add_enum(&mavenum);
1373                    }
1374                    Some(&MavXmlElement::Include) => {
1375                        let include_file = Path::new(&definitions_dir).join(include.clone());
1376                        if !parsed_files.contains(&include_file) {
1377                            let included_profile =
1378                                parse_profile(definitions_dir, &include, parsed_files)?;
1379                            for message in included_profile.messages.values() {
1380                                profile.add_message(message);
1381                            }
1382                            for enm in included_profile.enums.values() {
1383                                profile.add_enum(enm);
1384                            }
1385                        }
1386                    }
1387                    _ => (),
1388                }
1389                stack.pop();
1390                // println!("{}-{}", indent(depth), name);
1391            }
1392            Err(e) => {
1393                eprintln!("Error: {e}");
1394                break;
1395            }
1396            _ => {}
1397        }
1398    }
1399
1400    //let profile = profile.update_messages(); //TODO verify no longer needed
1401    Ok(profile.update_enums())
1402}
1403
1404/// Generate protobuf represenation of mavlink message set
1405/// Generate rust representation of mavlink message set with appropriate conversion methods
1406pub fn generate<W: Write>(
1407    definitions_dir: &Path,
1408    definition_file: &Path,
1409    output_rust: &mut W,
1410) -> Result<(), BindGenError> {
1411    let mut parsed_files: HashSet<PathBuf> = HashSet::new();
1412    let profile = parse_profile(definitions_dir, definition_file, &mut parsed_files)?;
1413
1414    // rust file
1415    let rust_tokens = profile.emit_rust();
1416    writeln!(output_rust, "{rust_tokens}").unwrap();
1417
1418    Ok(())
1419}
1420
1421/// CRC operates over names of the message and names of its fields
1422/// Hence we have to preserve the original uppercase names delimited with an underscore
1423/// For field names, we replace "type" with "mavtype" to make it rust compatible (this is
1424/// needed for generating sensible rust code), but for calculating crc function we have to
1425/// use the original name "type"
1426pub fn extra_crc(msg: &MavMessage) -> u8 {
1427    // calculate a 8-bit checksum of the key fields of a message, so we
1428    // can detect incompatible XML changes
1429    let mut crc = CRCu16::crc16mcrf4cc();
1430
1431    crc.digest(msg.name.as_bytes());
1432    crc.digest(b" ");
1433
1434    let mut f = msg.fields.clone();
1435    // only mavlink 1 fields should be part of the extra_crc
1436    f.retain(|f| !f.is_extension);
1437    f.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1438    for field in &f {
1439        crc.digest(field.mavtype.primitive_type().as_bytes());
1440        crc.digest(b" ");
1441        if field.name == "mavtype" {
1442            crc.digest(b"type");
1443        } else {
1444            crc.digest(field.name.as_bytes());
1445        }
1446        crc.digest(b" ");
1447        if let MavType::Array(_, size) = field.mavtype {
1448            crc.digest(&[size as u8]);
1449        }
1450    }
1451
1452    let crcval = crc.get_crc();
1453    ((crcval & 0xFF) ^ (crcval >> 8)) as u8
1454}
1455
1456#[cfg(not(feature = "emit-extensions"))]
1457struct ExtensionFilter {
1458    pub is_in: bool,
1459}
1460
1461struct MessageFilter {
1462    pub is_in: bool,
1463    pub messages: Vec<String>,
1464}
1465
1466impl MessageFilter {
1467    pub fn new() -> Self {
1468        Self {
1469            is_in: false,
1470            messages: vec![
1471                // device_cap_flags is u32, when enum is u16, which is not handled by the parser yet
1472                "STORM32_GIMBAL_MANAGER_INFORMATION".to_string(),
1473            ],
1474        }
1475    }
1476}
1477
1478struct MavXmlFilter {
1479    #[cfg(not(feature = "emit-extensions"))]
1480    extension_filter: ExtensionFilter,
1481    message_filter: MessageFilter,
1482}
1483
1484impl Default for MavXmlFilter {
1485    fn default() -> Self {
1486        Self {
1487            #[cfg(not(feature = "emit-extensions"))]
1488            extension_filter: ExtensionFilter { is_in: false },
1489            message_filter: MessageFilter::new(),
1490        }
1491    }
1492}
1493
1494impl MavXmlFilter {
1495    pub fn filter(&mut self, elements: &mut Vec<Result<Event, quick_xml::Error>>) {
1496        // List of filters
1497        elements.retain(|x| self.filter_extension(x));
1498        elements.retain(|x| self.filter_messages(x))
1499    }
1500
1501    #[cfg(feature = "emit-extensions")]
1502    pub fn filter_extension(&mut self, _element: &Result<Event, quick_xml::Error>) -> bool {
1503        true
1504    }
1505
1506    /// Ignore extension fields
1507    #[cfg(not(feature = "emit-extensions"))]
1508    pub fn filter_extension(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
1509        match element {
1510            Ok(content) => {
1511                match content {
1512                    Event::Start(bytes) | Event::Empty(bytes) => {
1513                        let Some(id) = identify_element(bytes.name().into_inner()) else {
1514                            panic!(
1515                                "unexpected element {:?}",
1516                                String::from_utf8_lossy(bytes.name().into_inner())
1517                            );
1518                        };
1519                        if id == MavXmlElement::Extensions {
1520                            self.extension_filter.is_in = true;
1521                        }
1522                    }
1523                    Event::End(bytes) => {
1524                        let Some(id) = identify_element(bytes.name().into_inner()) else {
1525                            panic!(
1526                                "unexpected element {:?}",
1527                                String::from_utf8_lossy(bytes.name().into_inner())
1528                            );
1529                        };
1530
1531                        if id == MavXmlElement::Message {
1532                            self.extension_filter.is_in = false;
1533                        }
1534                    }
1535                    _ => {}
1536                }
1537                !self.extension_filter.is_in
1538            }
1539            Err(error) => panic!("Failed to filter XML: {error}"),
1540        }
1541    }
1542
1543    /// Filters messages by their name
1544    pub fn filter_messages(&mut self, element: &Result<Event, quick_xml::Error>) -> bool {
1545        match element {
1546            Ok(content) => {
1547                match content {
1548                    Event::Start(bytes) | Event::Empty(bytes) => {
1549                        let Some(id) = identify_element(bytes.name().into_inner()) else {
1550                            panic!(
1551                                "unexpected element {:?}",
1552                                String::from_utf8_lossy(bytes.name().into_inner())
1553                            );
1554                        };
1555                        if id == MavXmlElement::Message {
1556                            for attr in bytes.attributes() {
1557                                let attr = attr.unwrap();
1558                                if attr.key.into_inner() == b"name" {
1559                                    let value = String::from_utf8_lossy(&attr.value).into_owned();
1560                                    if self.message_filter.messages.contains(&value) {
1561                                        self.message_filter.is_in = true;
1562                                        return false;
1563                                    }
1564                                }
1565                            }
1566                        }
1567                    }
1568                    Event::End(bytes) => {
1569                        let Some(id) = identify_element(bytes.name().into_inner()) else {
1570                            panic!(
1571                                "unexpected element {:?}",
1572                                String::from_utf8_lossy(bytes.name().into_inner())
1573                            );
1574                        };
1575
1576                        if id == MavXmlElement::Message && self.message_filter.is_in {
1577                            self.message_filter.is_in = false;
1578                            return false;
1579                        }
1580                    }
1581                    _ => {}
1582                }
1583                !self.message_filter.is_in
1584            }
1585            Err(error) => panic!("Failed to filter XML: {error}"),
1586        }
1587    }
1588}
1589
1590fn to_pascal_case(text: impl AsRef<[u8]>) -> String {
1591    text.as_ref()
1592        .split(|c| *c == b'_')
1593        .map(String::from_utf8_lossy)
1594        .map(capitalize_word)
1595        .collect()
1596}
1597
1598fn capitalize_word(text: impl AsRef<str>) -> String {
1599    let mut chars = text.as_ref().chars();
1600    match chars.next() {
1601        None => String::new(),
1602        Some(char) => char.to_uppercase().to_string() + &chars.as_str().to_ascii_lowercase(),
1603    }
1604}