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