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?://", r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", r"[a-zA-Z]{2,63}", r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*[-a-zA-Z0-9@:%_\+~#?&/=])?)" ))
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 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 if let Some(enm) = self.enums.get_mut(enum_name) {
83 if field.display == Some("bitmask".to_string()) {
85 enm.bitmask = true;
86 }
87
88 if enm.bitmask {
90 enm.primitive = Some(field.mavtype.rust_primitive_type());
91
92 if field.display.is_none() {
94 field.display = Some("bitmask".to_string());
95 }
96 }
97 }
98 }
99 }
100 }
101 self
102 }
103
104 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 fn emit_msgs(&self) -> Vec<TokenStream> {
126 self.messages.values().map(|d| d.emit_rust()).collect()
127 }
128
129 fn emit_enums(&self) -> Vec<TokenStream> {
131 self.enums.values().map(|d| d.emit_rust()).collect()
132 }
133
134 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 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 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 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 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 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 #[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 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 #[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 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 payload_buf[0..avail_len].copy_from_slice(__input);
622 Bytes::new(&payload_buf)
623 } else {
624 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 fn emit_name(&self) -> TokenStream {
731 let name = format_ident!("{}", self.name);
732 quote!(#name)
733 }
734
735 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 #[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 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 fn rust_writer(&self) -> TokenStream {
771 let mut name = "self.".to_string() + &self.name.clone();
772 if self.enumtype.is_some() {
773 if !matches!(self.mavtype, MavType::Array(_, _)) {
776 if let Some(dsp) = &self.display {
777 if dsp == "bitmask" {
779 name += ".bits()";
781 } else {
782 panic!("Display option not implemented");
783 }
784 } else {
785 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 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 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 let tmp = self.mavtype.rust_reader("e!(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 let tmp = self.mavtype.rust_reader("e!(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 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 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("e!(let val), buf);
920 quote! {
921 for v in &mut #val {
922 #r
923 *v = val;
924 }
925 }
926 }
927 }
928 }
929
930 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("e!(*val), buf);
948 quote! {
949 for val in &#val {
950 #w
951 }
952 }
953 }
954 }
955 }
956
957 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 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 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 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 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 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()); 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 } 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 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 = 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 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 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 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 not_extension_fields.sort_by(|a, b| a.mavtype.compare(&b.mavtype));
1395
1396 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 }
1425 Err(e) => {
1426 eprintln!("Error: {e}");
1427 break;
1428 }
1429 _ => {}
1430 }
1431 }
1432
1433 Ok(profile.update_enums())
1435}
1436
1437pub 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 let rust_tokens = profile.emit_rust(&dialect_name);
1451 writeln!(output_rust, "{rust_tokens}").unwrap();
1452
1453 Ok(())
1454}
1455
1456pub fn extra_crc(msg: &MavMessage) -> u8 {
1462 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 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 "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 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 #[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 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}