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