1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Rust compiler backend.
16
17 use crate::{analyzer, ast};
18 use quote::{format_ident, quote};
19 use std::collections::BTreeSet;
20 use std::collections::HashMap;
21 use std::path::Path;
22 use syn::LitInt;
23
24 mod parser;
25 mod preamble;
26 mod serializer;
27 pub mod test;
28 mod types;
29
30 use parser::FieldParser;
31 use serializer::FieldSerializer;
32
33 pub use heck::ToUpperCamelCase;
34
35 pub trait ToIdent {
36 /// Generate a sanitized rust identifier.
37 /// Rust specific keywords are renamed for validity.
to_ident(self) -> proc_macro2::Ident38 fn to_ident(self) -> proc_macro2::Ident;
39 }
40
41 impl ToIdent for &'_ str {
to_ident(self) -> proc_macro2::Ident42 fn to_ident(self) -> proc_macro2::Ident {
43 match self {
44 "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern"
45 | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod"
46 | "move" | "mut" | "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct"
47 | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while"
48 | "async" | "await" | "dyn" | "abstract" | "become" | "box" | "do" | "final"
49 | "macro" | "override" | "priv" | "typeof" | "unsized" | "virtual" | "yield"
50 | "try" => format_ident!("r#{}", self),
51 _ => format_ident!("{}", self),
52 }
53 }
54 }
55
56 /// Generate a bit-mask which masks out `n` least significant bits.
57 ///
58 /// Literal integers in Rust default to the `i32` type. For this
59 /// reason, if `n` is larger than 31, a suffix is added to the
60 /// `LitInt` returned. This should either be `u64` or `usize`
61 /// depending on where the result is used.
mask_bits(n: usize, suffix: &str) -> syn::LitInt62 pub fn mask_bits(n: usize, suffix: &str) -> syn::LitInt {
63 let suffix = if n > 31 { format!("_{suffix}") } else { String::new() };
64 // Format the hex digits as 0x1111_2222_3333_usize.
65 let hex_digits = format!("{:x}", (1u64 << n) - 1)
66 .as_bytes()
67 .rchunks(4)
68 .rev()
69 .map(|chunk| std::str::from_utf8(chunk).unwrap())
70 .collect::<Vec<&str>>()
71 .join("_");
72 syn::parse_str::<syn::LitInt>(&format!("0x{hex_digits}{suffix}")).unwrap()
73 }
74
generate_packet_size_getter<'a>( scope: &analyzer::Scope<'a>, schema: &analyzer::Schema, fields: impl Iterator<Item = &'a ast::Field>, is_packet: bool, ) -> (usize, proc_macro2::TokenStream)75 fn generate_packet_size_getter<'a>(
76 scope: &analyzer::Scope<'a>,
77 schema: &analyzer::Schema,
78 fields: impl Iterator<Item = &'a ast::Field>,
79 is_packet: bool,
80 ) -> (usize, proc_macro2::TokenStream) {
81 let mut constant_width = 0;
82 let mut dynamic_widths = Vec::new();
83
84 for field in fields {
85 if let Some(width) =
86 schema.padded_size(field.key).or(schema.field_size(field.key).static_())
87 {
88 constant_width += width;
89 continue;
90 }
91
92 let decl = scope.get_type_declaration(field);
93 dynamic_widths.push(match &field.desc {
94 ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body { .. } => {
95 if is_packet {
96 quote! {
97 self.child.get_total_size()
98 }
99 } else {
100 quote! {
101 self.payload.len()
102 }
103 }
104 }
105 ast::FieldDesc::Scalar { id, width } => {
106 assert!(field.cond.is_some());
107 let id = id.to_ident();
108 let width = syn::Index::from(*width / 8);
109 quote!(if self.#id.is_some() { #width } else { 0 })
110 }
111 ast::FieldDesc::Typedef { id, type_id, .. } if field.cond.is_some() => {
112 let id = id.to_ident();
113 match &scope.typedef[type_id].desc {
114 ast::DeclDesc::Enum { width, .. } => {
115 let width = syn::Index::from(*width / 8);
116 quote!(if self.#id.is_some() { #width } else { 0 })
117 }
118 _ => {
119 let type_id = type_id.to_ident();
120 quote! {
121 self.#id
122 .as_ref()
123 .map(#type_id::get_size)
124 .unwrap_or(0)
125 }
126 }
127 }
128 }
129 ast::FieldDesc::Typedef { id, .. } => {
130 let id = id.to_ident();
131 quote!(self.#id.get_size())
132 }
133 ast::FieldDesc::Array { id, width, .. } => {
134 let id = id.to_ident();
135 match &decl {
136 Some(ast::Decl {
137 desc: ast::DeclDesc::Struct { .. } | ast::DeclDesc::CustomField { .. },
138 ..
139 }) => {
140 quote! {
141 self.#id.iter().map(|elem| elem.get_size()).sum::<usize>()
142 }
143 }
144 Some(ast::Decl { desc: ast::DeclDesc::Enum { width, .. }, .. }) => {
145 let width = syn::Index::from(width / 8);
146 let mul_width = (width.index > 1).then(|| quote!(* #width));
147 quote! {
148 self.#id.len() #mul_width
149 }
150 }
151 _ => {
152 let width = syn::Index::from(width.unwrap() / 8);
153 let mul_width = (width.index > 1).then(|| quote!(* #width));
154 quote! {
155 self.#id.len() #mul_width
156 }
157 }
158 }
159 }
160 _ => panic!("Unsupported field type: {field:?}"),
161 });
162 }
163
164 if constant_width > 0 {
165 let width = syn::Index::from(constant_width / 8);
166 dynamic_widths.insert(0, quote!(#width));
167 }
168 if dynamic_widths.is_empty() {
169 dynamic_widths.push(quote!(0))
170 }
171
172 (
173 constant_width,
174 quote! {
175 #(#dynamic_widths)+*
176 },
177 )
178 }
179
top_level_packet<'a>(scope: &analyzer::Scope<'a>, packet_name: &'a str) -> &'a ast::Decl180 fn top_level_packet<'a>(scope: &analyzer::Scope<'a>, packet_name: &'a str) -> &'a ast::Decl {
181 let mut decl = scope.typedef[packet_name];
182 while let ast::DeclDesc::Packet { parent_id: Some(parent_id), .. }
183 | ast::DeclDesc::Struct { parent_id: Some(parent_id), .. } = &decl.desc
184 {
185 decl = scope.typedef[parent_id];
186 }
187 decl
188 }
189
190 /// Find parent fields which are constrained in child packets.
191 ///
192 /// These fields are the fields which need to be passed in when
193 /// parsing a `id` packet since their values are needed for one or
194 /// more child packets.
find_constrained_parent_fields<'a>( scope: &analyzer::Scope<'a>, id: &str, ) -> Vec<&'a ast::Field>195 fn find_constrained_parent_fields<'a>(
196 scope: &analyzer::Scope<'a>,
197 id: &str,
198 ) -> Vec<&'a ast::Field> {
199 let all_parent_fields: HashMap<String, &'a ast::Field> = HashMap::from_iter(
200 scope
201 .iter_parent_fields(scope.typedef[id])
202 .filter_map(|f| f.id().map(|id| (id.to_string(), f))),
203 );
204
205 let mut fields = Vec::new();
206 let mut field_names = BTreeSet::new();
207 let mut children = scope.iter_children(scope.typedef[id]).collect::<Vec<_>>();
208
209 while let Some(child) = children.pop() {
210 if let ast::DeclDesc::Packet { id, constraints, .. }
211 | ast::DeclDesc::Struct { id, constraints, .. } = &child.desc
212 {
213 for constraint in constraints {
214 if field_names.insert(&constraint.id)
215 && all_parent_fields.contains_key(&constraint.id)
216 {
217 fields.push(all_parent_fields[&constraint.id]);
218 }
219 }
220 children.extend(scope.iter_children(scope.typedef[id]).collect::<Vec<_>>());
221 }
222 }
223
224 fields
225 }
226
227 /// Generate the declaration and implementation for a data struct.
228 ///
229 /// This struct will hold the data for a packet or a struct. It knows
230 /// how to parse and serialize its own fields.
generate_data_struct( scope: &analyzer::Scope<'_>, schema: &analyzer::Schema, endianness: ast::EndiannessValue, id: &str, ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream)231 fn generate_data_struct(
232 scope: &analyzer::Scope<'_>,
233 schema: &analyzer::Schema,
234 endianness: ast::EndiannessValue,
235 id: &str,
236 ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
237 let decl = scope.typedef[id];
238 let is_packet = matches!(&decl.desc, ast::DeclDesc::Packet { .. });
239
240 let span = format_ident!("bytes");
241 let serializer_span = format_ident!("buffer");
242 let mut field_parser = FieldParser::new(scope, schema, endianness, id, &span);
243 let mut field_serializer =
244 FieldSerializer::new(scope, schema, endianness, id, &serializer_span);
245 for field in decl.fields() {
246 field_parser.add(field);
247 field_serializer.add(field);
248 }
249 field_parser.done();
250
251 let (parse_arg_names, parse_arg_types) = if is_packet {
252 let fields = find_constrained_parent_fields(scope, id);
253 let names = fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
254 let types = fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
255 (names, types)
256 } else {
257 (Vec::new(), Vec::new()) // No extra arguments to parse in structs.
258 };
259
260 let (constant_width, packet_size) =
261 generate_packet_size_getter(scope, schema, decl.fields(), is_packet);
262 let conforms = if constant_width == 0 {
263 quote! { true }
264 } else {
265 let constant_width = syn::Index::from(constant_width / 8);
266 quote! { #span.len() >= #constant_width }
267 };
268
269 let visibility = if is_packet { quote!() } else { quote!(pub) };
270 let has_payload = decl.payload().is_some();
271 let has_children = scope.iter_children(decl).next().is_some();
272
273 let struct_name = if is_packet { format_ident!("{id}Data") } else { id.to_ident() };
274 let backed_fields = decl
275 .fields()
276 .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
277 .collect::<Vec<_>>();
278
279 let mut field_names =
280 backed_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
281 let mut field_types = backed_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
282
283 if has_children || has_payload {
284 if is_packet {
285 field_names.push(format_ident!("child"));
286 let field_type = format_ident!("{id}DataChild");
287 field_types.push(quote!(#field_type));
288 } else {
289 field_names.push(format_ident!("payload"));
290 field_types.push(quote!(Vec<u8>));
291 }
292 }
293
294 let data_struct_decl = quote! {
295 #[derive(Debug, Clone, PartialEq, Eq)]
296 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297 pub struct #struct_name {
298 #(#visibility #field_names: #field_types,)*
299 }
300 };
301
302 let data_struct_impl = quote! {
303 impl #struct_name {
304 fn conforms(#span: &[u8]) -> bool {
305 #conforms
306 }
307
308 #visibility fn parse(
309 #span: &[u8] #(, #parse_arg_names: #parse_arg_types)*
310 ) -> Result<Self, DecodeError> {
311 let mut cell = Cell::new(#span);
312 let packet = Self::parse_inner(&mut cell #(, #parse_arg_names)*)?;
313 // TODO(mgeisler): communicate back to user if !cell.get().is_empty()?
314 Ok(packet)
315 }
316
317 fn parse_inner(
318 mut #span: &mut Cell<&[u8]> #(, #parse_arg_names: #parse_arg_types)*
319 ) -> Result<Self, DecodeError> {
320 #field_parser
321 Ok(Self {
322 #(#field_names,)*
323 })
324 }
325
326 fn write_to<T: BufMut>(&self, buffer: &mut T) -> Result<(), EncodeError> {
327 #field_serializer
328 Ok(())
329 }
330
331 fn get_total_size(&self) -> usize {
332 self.get_size()
333 }
334
335 fn get_size(&self) -> usize {
336 #packet_size
337 }
338 }
339 };
340
341 (data_struct_decl, data_struct_impl)
342 }
343
344 /// Turn the constraint into a value (such as `10` or
345 /// `SomeEnum::Foo`).
constraint_to_value( all_fields: &HashMap<String, &'_ ast::Field>, constraint: &ast::Constraint, ) -> proc_macro2::TokenStream346 pub fn constraint_to_value(
347 all_fields: &HashMap<String, &'_ ast::Field>,
348 constraint: &ast::Constraint,
349 ) -> proc_macro2::TokenStream {
350 match constraint {
351 ast::Constraint { value: Some(value), .. } => {
352 let value = proc_macro2::Literal::usize_unsuffixed(*value);
353 quote!(#value)
354 }
355 // TODO(mgeisler): include type_id in `ast::Constraint` and
356 // drop the packet_scope argument.
357 ast::Constraint { tag_id: Some(tag_id), .. } => {
358 let type_id = match &all_fields[&constraint.id].desc {
359 ast::FieldDesc::Typedef { type_id, .. } => type_id.to_ident(),
360 _ => unreachable!("Invalid constraint: {constraint:?}"),
361 };
362 let tag_id = format_ident!("{}", tag_id.to_upper_camel_case());
363 quote!(#type_id::#tag_id)
364 }
365 _ => unreachable!("Invalid constraint: {constraint:?}"),
366 }
367 }
368
369 /// Generate code for a `ast::Decl::Packet`.
generate_packet_decl( scope: &analyzer::Scope<'_>, schema: &analyzer::Schema, endianness: ast::EndiannessValue, id: &str, ) -> proc_macro2::TokenStream370 fn generate_packet_decl(
371 scope: &analyzer::Scope<'_>,
372 schema: &analyzer::Schema,
373 endianness: ast::EndiannessValue,
374 id: &str,
375 ) -> proc_macro2::TokenStream {
376 let decl = scope.typedef[id];
377 let top_level = top_level_packet(scope, id);
378 let top_level_id = top_level.id().unwrap();
379 let top_level_packet = top_level_id.to_ident();
380 let top_level_data = format_ident!("{top_level_id}Data");
381 let top_level_id_lower = top_level_id.to_lowercase().to_ident();
382
383 // TODO(mgeisler): use the convert_case crate to convert between
384 // `FooBar` and `foo_bar` in the code below.
385 let span = format_ident!("bytes");
386 let id_lower = id.to_lowercase().to_ident();
387 let id_packet = id.to_ident();
388 let id_child = format_ident!("{id}Child");
389 let id_data_child = format_ident!("{id}DataChild");
390 let id_builder = format_ident!("{id}Builder");
391
392 let mut parents = scope.iter_parents_and_self(decl).collect::<Vec<_>>();
393 parents.reverse();
394
395 let parent_ids = parents.iter().map(|p| p.id().unwrap()).collect::<Vec<_>>();
396 let parent_shifted_ids = parent_ids.iter().skip(1).map(|id| id.to_ident());
397 let parent_lower_ids =
398 parent_ids.iter().map(|id| id.to_lowercase().to_ident()).collect::<Vec<_>>();
399 let parent_shifted_lower_ids = parent_lower_ids.iter().skip(1).collect::<Vec<_>>();
400 let parent_packet = parent_ids.iter().map(|id| id.to_ident());
401 let parent_data = parent_ids.iter().map(|id| format_ident!("{id}Data"));
402 let parent_data_child = parent_ids.iter().map(|id| format_ident!("{id}DataChild"));
403
404 let all_fields = {
405 let mut fields = scope
406 .iter_fields(decl)
407 .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
408 .collect::<Vec<_>>();
409 fields.sort_by_key(|f| f.id());
410 fields
411 };
412 let all_named_fields =
413 HashMap::from_iter(all_fields.iter().map(|f| (f.id().unwrap().to_string(), *f)));
414
415 let all_field_names = all_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
416 let all_field_types = all_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
417 let all_field_borrows =
418 all_fields.iter().map(|f| types::rust_borrow(f, scope)).collect::<Vec<_>>();
419 let all_field_getter_names =
420 all_fields.iter().map(|f| format_ident!("get_{}", f.id().unwrap()));
421 let all_field_self_field = all_fields.iter().map(|f| {
422 for (parent, parent_id) in parents.iter().zip(parent_lower_ids.iter()) {
423 if parent.fields().any(|ff| ff.id() == f.id()) {
424 return quote!(self.#parent_id);
425 }
426 }
427 unreachable!("Could not find {f:?} in parent chain");
428 });
429
430 let all_constraints = HashMap::<String, _>::from_iter(
431 scope.iter_constraints(decl).map(|c| (c.id.to_string(), c)),
432 );
433
434 let unconstrained_fields = all_fields
435 .iter()
436 .filter(|f| !all_constraints.contains_key(f.id().unwrap()))
437 .collect::<Vec<_>>();
438 let unconstrained_field_names =
439 unconstrained_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
440 let unconstrained_field_types = unconstrained_fields.iter().map(|f| types::rust_type(f));
441
442 let rev_parents = parents.iter().rev().collect::<Vec<_>>();
443 let builder_assignments = rev_parents.iter().enumerate().map(|(idx, parent)| {
444 let parent_id = parent.id().unwrap();
445 let parent_id_lower = parent_id.to_lowercase().to_ident();
446 let parent_data = format_ident!("{parent_id}Data");
447 let parent_data_child = format_ident!("{parent_id}DataChild");
448
449 let named_fields = {
450 let mut names = parent
451 .fields()
452 .filter(|f| !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
453 .filter_map(ast::Field::id)
454 .collect::<Vec<_>>();
455 names.sort_unstable();
456 names
457 };
458
459 let mut field = named_fields.iter().map(|id| id.to_ident()).collect::<Vec<_>>();
460 let mut value = named_fields
461 .iter()
462 .map(|&id| match all_constraints.get(id) {
463 Some(constraint) => constraint_to_value(&all_named_fields, constraint),
464 None => {
465 let id = id.to_ident();
466 quote!(self.#id)
467 }
468 })
469 .collect::<Vec<_>>();
470
471 if parent.payload().is_some() {
472 field.push(format_ident!("child"));
473 if idx == 0 {
474 // Top-most parent, the child is simply created from
475 // our payload.
476 value.push(quote! {
477 match self.payload {
478 None => #parent_data_child::None,
479 Some(bytes) => #parent_data_child::Payload(bytes),
480 }
481 });
482 } else {
483 // Child is created from the previous parent.
484 let prev_parent_id = rev_parents[idx - 1].id().unwrap();
485 let prev_parent_id_lower = prev_parent_id.to_lowercase().to_ident();
486 let prev_parent_id = prev_parent_id.to_ident();
487 value.push(quote! {
488 #parent_data_child::#prev_parent_id(#prev_parent_id_lower)
489 });
490 }
491 } else if scope.iter_children(parent).next().is_some() {
492 field.push(format_ident!("child"));
493 value.push(quote! { #parent_data_child::None });
494 }
495
496 quote! {
497 let #parent_id_lower = #parent_data {
498 #(#field: #value,)*
499 };
500 }
501 });
502
503 let children = scope.iter_children(decl).collect::<Vec<_>>();
504 let has_payload = decl.payload().is_some();
505 let has_children_or_payload = !children.is_empty() || has_payload;
506 let child = children.iter().map(|child| child.id().unwrap().to_ident()).collect::<Vec<_>>();
507 let child_data = child.iter().map(|child| format_ident!("{child}Data")).collect::<Vec<_>>();
508 let get_payload = (children.is_empty() && has_payload).then(|| {
509 quote! {
510 pub fn get_payload(&self) -> &[u8] {
511 match &self.#id_lower.child {
512 #id_data_child::Payload(bytes) => &bytes,
513 #id_data_child::None => &[],
514 }
515 }
516 }
517 });
518 let child_declaration = has_children_or_payload.then(|| {
519 quote! {
520 #[derive(Debug, Clone, PartialEq, Eq)]
521 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
522 pub enum #id_data_child {
523 #(#child(#child_data),)*
524 Payload(Bytes),
525 None,
526 }
527
528 impl #id_data_child {
529 fn get_total_size(&self) -> usize {
530 match self {
531 #(#id_data_child::#child(value) => value.get_total_size(),)*
532 #id_data_child::Payload(bytes) => bytes.len(),
533 #id_data_child::None => 0,
534 }
535 }
536 }
537
538 #[derive(Debug, Clone, PartialEq, Eq)]
539 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
540 pub enum #id_child {
541 #(#child(#child),)*
542 Payload(Bytes),
543 None,
544 }
545 }
546 });
547 let specialize = has_children_or_payload.then(|| {
548 quote! {
549 pub fn specialize(&self) -> #id_child {
550 match &self.#id_lower.child {
551 #(
552 #id_data_child::#child(_) =>
553 #id_child::#child(#child::new(self.#top_level_id_lower.clone()).unwrap()),
554 )*
555 #id_data_child::Payload(payload) => #id_child::Payload(payload.clone()),
556 #id_data_child::None => #id_child::None,
557 }
558 }
559 }
560 });
561
562 let builder_payload_field = has_children_or_payload.then(|| {
563 quote! {
564 pub payload: Option<Bytes>
565 }
566 });
567
568 let ancestor_packets = parent_ids[..parent_ids.len() - 1].iter().map(|id| id.to_ident());
569 let impl_from_and_try_from = (top_level_id != id).then(|| {
570 quote! {
571 #(
572 impl From<#id_packet> for #ancestor_packets {
573 fn from(packet: #id_packet) -> #ancestor_packets {
574 #ancestor_packets::new(packet.#top_level_id_lower).unwrap()
575 }
576 }
577 )*
578
579 impl TryFrom<#top_level_packet> for #id_packet {
580 type Error = DecodeError;
581 fn try_from(packet: #top_level_packet) -> Result<#id_packet, Self::Error> {
582 #id_packet::new(packet.#top_level_id_lower)
583 }
584 }
585 }
586 });
587
588 let (data_struct_decl, data_struct_impl) = generate_data_struct(scope, schema, endianness, id);
589
590 quote! {
591 #child_declaration
592
593 #data_struct_decl
594
595 #[derive(Debug, Clone, PartialEq, Eq)]
596 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597 pub struct #id_packet {
598 #(
599 #[cfg_attr(feature = "serde", serde(flatten))]
600 #parent_lower_ids: #parent_data,
601 )*
602 }
603
604 #[derive(Debug)]
605 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
606 pub struct #id_builder {
607 #(pub #unconstrained_field_names: #unconstrained_field_types,)*
608 #builder_payload_field
609 }
610
611 #data_struct_impl
612
613 impl Packet for #id_packet {
614 fn encoded_len(&self) -> usize {
615 self.get_size()
616 }
617 fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> {
618 self.#top_level_id_lower.write_to(buf)
619 }
620 fn decode(_: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
621 unimplemented!("Rust legacy does not implement full packet trait")
622 }
623 }
624
625 impl TryFrom<#id_packet> for Bytes {
626 type Error = EncodeError;
627 fn try_from(packet: #id_packet) -> Result<Self, Self::Error> {
628 packet.encode_to_bytes()
629 }
630 }
631
632 impl TryFrom<#id_packet> for Vec<u8> {
633 type Error = EncodeError;
634 fn try_from(packet: #id_packet) -> Result<Self, Self::Error> {
635 packet.encode_to_vec()
636 }
637 }
638
639 #impl_from_and_try_from
640
641 impl #id_packet {
642 pub fn parse(#span: &[u8]) -> Result<Self, DecodeError> {
643 let mut cell = Cell::new(#span);
644 let packet = Self::parse_inner(&mut cell)?;
645 // TODO(mgeisler): communicate back to user if !cell.get().is_empty()?
646 Ok(packet)
647 }
648
649 fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self, DecodeError> {
650 let data = #top_level_data::parse_inner(&mut bytes)?;
651 Self::new(data)
652 }
653
654 #specialize
655
656 fn new(#top_level_id_lower: #top_level_data) -> Result<Self, DecodeError> {
657 #(
658 let #parent_shifted_lower_ids = match &#parent_lower_ids.child {
659 #parent_data_child::#parent_shifted_ids(value) => value.clone(),
660 _ => return Err(DecodeError::InvalidChildError {
661 expected: stringify!(#parent_data_child::#parent_shifted_ids),
662 actual: format!("{:?}", &#parent_lower_ids.child),
663 }),
664 };
665 )*
666 Ok(Self { #(#parent_lower_ids),* })
667 }
668
669 #(pub fn #all_field_getter_names(&self) -> #all_field_borrows #all_field_types {
670 #all_field_borrows #all_field_self_field.#all_field_names
671 })*
672
673 #get_payload
674
675 fn write_to(&self, buffer: &mut impl BufMut) -> Result<(), EncodeError> {
676 self.#id_lower.write_to(buffer)
677 }
678
679 pub fn get_size(&self) -> usize {
680 self.#top_level_id_lower.get_size()
681 }
682 }
683
684 impl #id_builder {
685 pub fn build(self) -> #id_packet {
686 #(#builder_assignments;)*
687 #id_packet::new(#top_level_id_lower).unwrap()
688 }
689 }
690
691 #(
692 impl From<#id_builder> for #parent_packet {
693 fn from(builder: #id_builder) -> #parent_packet {
694 builder.build().into()
695 }
696 }
697 )*
698 }
699 }
700
701 /// Generate code for a `ast::Decl::Struct`.
generate_struct_decl( scope: &analyzer::Scope<'_>, schema: &analyzer::Schema, endianness: ast::EndiannessValue, id: &str, ) -> proc_macro2::TokenStream702 fn generate_struct_decl(
703 scope: &analyzer::Scope<'_>,
704 schema: &analyzer::Schema,
705 endianness: ast::EndiannessValue,
706 id: &str,
707 ) -> proc_macro2::TokenStream {
708 let (struct_decl, struct_impl) = generate_data_struct(scope, schema, endianness, id);
709 quote! {
710 #struct_decl
711 #struct_impl
712 }
713 }
714
715 /// Generate an enum declaration.
716 ///
717 /// # Arguments
718 /// * `id` - Enum identifier.
719 /// * `tags` - List of enum tags.
720 /// * `width` - Width of the backing type of the enum, in bits.
721 /// * `open` - Whether to generate an open or closed enum. Open enums have
722 /// an additional Unknown case for unmatched valued. Complete
723 /// enums (where the full range of values is covered) are
724 /// automatically closed.
generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2::TokenStream725 fn generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2::TokenStream {
726 // Determine if the enum is open, i.e. a default tag is defined.
727 fn enum_default_tag(tags: &[ast::Tag]) -> Option<ast::TagOther> {
728 tags.iter()
729 .filter_map(|tag| match tag {
730 ast::Tag::Other(tag) => Some(tag.clone()),
731 _ => None,
732 })
733 .next()
734 }
735
736 // Determine if the enum is complete, i.e. all values in the backing
737 // integer range have a matching tag in the original declaration.
738 fn enum_is_complete(tags: &[ast::Tag], max: usize) -> bool {
739 let mut ranges = tags
740 .iter()
741 .filter_map(|tag| match tag {
742 ast::Tag::Value(tag) => Some((tag.value, tag.value)),
743 ast::Tag::Range(tag) => Some(tag.range.clone().into_inner()),
744 _ => None,
745 })
746 .collect::<Vec<_>>();
747 ranges.sort_unstable();
748 ranges.first().unwrap().0 == 0
749 && ranges.last().unwrap().1 == max
750 && ranges.windows(2).all(|window| {
751 if let [left, right] = window {
752 left.1 == right.0 - 1
753 } else {
754 false
755 }
756 })
757 }
758
759 // Determine if the enum is primitive, i.e. does not contain any tag range.
760 fn enum_is_primitive(tags: &[ast::Tag]) -> bool {
761 tags.iter().all(|tag| matches!(tag, ast::Tag::Value(_)))
762 }
763
764 // Return the maximum value for the scalar type.
765 fn scalar_max(width: usize) -> usize {
766 if width >= usize::BITS as usize {
767 usize::MAX
768 } else {
769 (1 << width) - 1
770 }
771 }
772
773 // Format an enum tag identifier to rust upper caml case.
774 fn format_tag_ident(id: &str) -> proc_macro2::TokenStream {
775 let id = format_ident!("{}", id.to_upper_camel_case());
776 quote! { #id }
777 }
778
779 // Format a constant value as hexadecimal constant.
780 fn format_value(value: usize) -> LitInt {
781 syn::parse_str::<syn::LitInt>(&format!("{:#x}", value)).unwrap()
782 }
783
784 // Backing type for the enum.
785 let backing_type = types::Integer::new(width);
786 let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width));
787 let range_max = scalar_max(width);
788 let default_tag = enum_default_tag(tags);
789 let is_open = default_tag.is_some();
790 let is_complete = enum_is_complete(tags, scalar_max(width));
791 let is_primitive = enum_is_primitive(tags);
792 let name = id.to_ident();
793
794 // Generate the variant cases for the enum declaration.
795 // Tags declared in ranges are flattened in the same declaration.
796 let use_variant_values = is_primitive && (is_complete || !is_open);
797 let repr_u64 = use_variant_values.then(|| quote! { #[repr(u64)] });
798 let mut variants = vec![];
799 for tag in tags.iter() {
800 match tag {
801 ast::Tag::Value(tag) if use_variant_values => {
802 let id = format_tag_ident(&tag.id);
803 let value = format_value(tag.value);
804 variants.push(quote! { #id = #value })
805 }
806 ast::Tag::Value(tag) => variants.push(format_tag_ident(&tag.id)),
807 ast::Tag::Range(tag) => {
808 variants.extend(tag.tags.iter().map(|tag| format_tag_ident(&tag.id)));
809 let id = format_tag_ident(&tag.id);
810 variants.push(quote! { #id(Private<#backing_type>) })
811 }
812 ast::Tag::Other(_) => (),
813 }
814 }
815
816 // Generate the cases for parsing the enum value from an integer.
817 let mut from_cases = vec![];
818 for tag in tags.iter() {
819 match tag {
820 ast::Tag::Value(tag) => {
821 let id = format_tag_ident(&tag.id);
822 let value = format_value(tag.value);
823 from_cases.push(quote! { #value => Ok(#name::#id) })
824 }
825 ast::Tag::Range(tag) => {
826 from_cases.extend(tag.tags.iter().map(|tag| {
827 let id = format_tag_ident(&tag.id);
828 let value = format_value(tag.value);
829 quote! { #value => Ok(#name::#id) }
830 }));
831 let id = format_tag_ident(&tag.id);
832 let start = format_value(*tag.range.start());
833 let end = format_value(*tag.range.end());
834 from_cases.push(quote! { #start ..= #end => Ok(#name::#id(Private(value))) })
835 }
836 ast::Tag::Other(_) => (),
837 }
838 }
839
840 // Generate the cases for serializing the enum value to an integer.
841 let mut into_cases = vec![];
842 for tag in tags.iter() {
843 match tag {
844 ast::Tag::Value(tag) => {
845 let id = format_tag_ident(&tag.id);
846 let value = format_value(tag.value);
847 into_cases.push(quote! { #name::#id => #value })
848 }
849 ast::Tag::Range(tag) => {
850 into_cases.extend(tag.tags.iter().map(|tag| {
851 let id = format_tag_ident(&tag.id);
852 let value = format_value(tag.value);
853 quote! { #name::#id => #value }
854 }));
855 let id = format_tag_ident(&tag.id);
856 into_cases.push(quote! { #name::#id(Private(value)) => *value })
857 }
858 ast::Tag::Other(_) => (),
859 }
860 }
861
862 // Generate a default case if the enum is open and incomplete.
863 if !is_complete && is_open {
864 let unknown_id = format_tag_ident(&default_tag.unwrap().id);
865 let range_max = format_value(range_max);
866 variants.push(quote! { #unknown_id(Private<#backing_type>) });
867 from_cases.push(quote! { 0..=#range_max => Ok(#name::#unknown_id(Private(value))) });
868 into_cases.push(quote! { #name::#unknown_id(Private(value)) => *value });
869 }
870
871 // Generate an error case if the enum size is lower than the backing
872 // type size, or if the enum is closed or incomplete.
873 if backing_type.width != width || (!is_complete && !is_open) {
874 from_cases.push(quote! { _ => Err(value) });
875 }
876
877 // Derive other Into<uN> and Into<iN> implementations from the explicit
878 // implementation, where the type is larger than the backing type.
879 let derived_signed_into_types = [8, 16, 32, 64]
880 .into_iter()
881 .filter(|w| *w > width)
882 .map(|w| syn::parse_str::<syn::Type>(&format!("i{}", w)).unwrap());
883 let derived_unsigned_into_types = [8, 16, 32, 64]
884 .into_iter()
885 .filter(|w| *w >= width && *w != backing_type.width)
886 .map(|w| syn::parse_str::<syn::Type>(&format!("u{}", w)).unwrap());
887 let derived_into_types = derived_signed_into_types.chain(derived_unsigned_into_types);
888
889 quote! {
890 #repr_u64
891 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
892 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
893 #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))]
894 pub enum #name {
895 #(#variants,)*
896 }
897
898 impl TryFrom<#backing_type> for #name {
899 type Error = #backing_type;
900 fn try_from(value: #backing_type) -> Result<Self, Self::Error> {
901 match value {
902 #(#from_cases,)*
903 }
904 }
905 }
906
907 impl From<&#name> for #backing_type {
908 fn from(value: &#name) -> Self {
909 match value {
910 #(#into_cases,)*
911 }
912 }
913 }
914
915 impl From<#name> for #backing_type {
916 fn from(value: #name) -> Self {
917 (&value).into()
918 }
919 }
920
921 #(impl From<#name> for #derived_into_types {
922 fn from(value: #name) -> Self {
923 #backing_type::from(value) as Self
924 }
925 })*
926 }
927 }
928
929 /// Generate the declaration for a custom field of static size.
930 ///
931 /// * `id` - Enum identifier.
932 /// * `width` - Width of the backing type of the enum, in bits.
generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream933 fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream {
934 let id = id.to_ident();
935 let backing_type = types::Integer::new(width);
936 let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width));
937 let max_value = mask_bits(width, &format!("u{}", backing_type.width));
938 let common = quote! {
939 impl From<&#id> for #backing_type {
940 fn from(value: &#id) -> #backing_type {
941 value.0
942 }
943 }
944
945 impl From<#id> for #backing_type {
946 fn from(value: #id) -> #backing_type {
947 value.0
948 }
949 }
950 };
951
952 if backing_type.width == width {
953 quote! {
954 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
955 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
956 #[cfg_attr(feature = "serde", serde(from = #backing_type_str, into = #backing_type_str))]
957 pub struct #id(#backing_type);
958
959 #common
960
961 impl From<#backing_type> for #id {
962 fn from(value: #backing_type) -> Self {
963 #id(value)
964 }
965 }
966 }
967 } else {
968 quote! {
969 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
970 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
971 #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))]
972 pub struct #id(#backing_type);
973
974 #common
975
976 impl TryFrom<#backing_type> for #id {
977 type Error = #backing_type;
978 fn try_from(value: #backing_type) -> Result<Self, Self::Error> {
979 if value > #max_value {
980 Err(value)
981 } else {
982 Ok(#id(value))
983 }
984 }
985 }
986 }
987 }
988 }
989
generate_decl( scope: &analyzer::Scope<'_>, schema: &analyzer::Schema, file: &ast::File, decl: &ast::Decl, ) -> proc_macro2::TokenStream990 fn generate_decl(
991 scope: &analyzer::Scope<'_>,
992 schema: &analyzer::Schema,
993 file: &ast::File,
994 decl: &ast::Decl,
995 ) -> proc_macro2::TokenStream {
996 match &decl.desc {
997 ast::DeclDesc::Packet { id, .. } => {
998 generate_packet_decl(scope, schema, file.endianness.value, id)
999 }
1000 ast::DeclDesc::Struct { id, parent_id: None, .. } => {
1001 // TODO(mgeisler): handle structs with parents. We could
1002 // generate code for them, but the code is not useful
1003 // since it would require the caller to unpack everything
1004 // manually. We either need to change the API, or
1005 // implement the recursive (de)serialization.
1006 generate_struct_decl(scope, schema, file.endianness.value, id)
1007 }
1008 ast::DeclDesc::Enum { id, tags, width } => generate_enum_decl(id, tags, *width),
1009 ast::DeclDesc::CustomField { id, width: Some(width), .. } => {
1010 generate_custom_field_decl(id, *width)
1011 }
1012 _ => todo!("unsupported Decl::{:?}", decl),
1013 }
1014 }
1015
1016 /// Generate Rust code from an AST.
1017 ///
1018 /// The code is not formatted, pipe it through `rustfmt` to get
1019 /// readable source code.
generate_tokens( sources: &ast::SourceDatabase, file: &ast::File, ) -> proc_macro2::TokenStream1020 pub fn generate_tokens(
1021 sources: &ast::SourceDatabase,
1022 file: &ast::File,
1023 ) -> proc_macro2::TokenStream {
1024 let source = sources.get(file.file).expect("could not read source");
1025 let preamble = preamble::generate(Path::new(source.name()));
1026
1027 let scope = analyzer::Scope::new(file).expect("could not create scope");
1028 let schema = analyzer::Schema::new(file);
1029 let decls = file.declarations.iter().map(|decl| generate_decl(&scope, &schema, file, decl));
1030 quote! {
1031 #preamble
1032
1033 #(#decls)*
1034 }
1035 }
1036
1037 /// Generate formatted Rust code from an AST.
1038 ///
1039 /// The code is not formatted, pipe it through `rustfmt` to get
1040 /// readable source code.
generate(sources: &ast::SourceDatabase, file: &ast::File) -> String1041 pub fn generate(sources: &ast::SourceDatabase, file: &ast::File) -> String {
1042 let syntax_tree = syn::parse2(generate_tokens(sources, file)).expect("Could not parse code");
1043 prettyplease::unparse(&syntax_tree)
1044 }
1045
1046 #[cfg(test)]
1047 mod tests {
1048 use super::*;
1049 use crate::analyzer;
1050 use crate::ast;
1051 use crate::parser::parse_inline;
1052 use crate::test_utils::{assert_snapshot_eq, format_rust};
1053 use googletest::prelude::{elements_are, eq, expect_that};
1054 use paste::paste;
1055
1056 /// Parse a string fragment as a PDL file.
1057 ///
1058 /// # Panics
1059 ///
1060 /// Panics on parse errors.
parse_str(text: &str) -> ast::File1061 pub fn parse_str(text: &str) -> ast::File {
1062 let mut db = ast::SourceDatabase::new();
1063 let file = parse_inline(&mut db, "stdin", String::from(text)).expect("parse error");
1064 analyzer::analyze(&file).expect("analyzer error")
1065 }
1066
1067 #[googletest::test]
test_find_constrained_parent_fields() -> googletest::Result<()>1068 fn test_find_constrained_parent_fields() -> googletest::Result<()> {
1069 let code = "
1070 little_endian_packets
1071 packet Parent {
1072 a: 8,
1073 b: 8,
1074 c: 8,
1075 _payload_,
1076 }
1077 packet Child: Parent(a = 10) {
1078 x: 8,
1079 _payload_,
1080 }
1081 packet GrandChild: Child(b = 20) {
1082 y: 8,
1083 _payload_,
1084 }
1085 packet GrandGrandChild: GrandChild(c = 30) {
1086 z: 8,
1087 }
1088 ";
1089 let file = parse_str(code);
1090 let scope = analyzer::Scope::new(&file).unwrap();
1091 let find_fields = |id| {
1092 find_constrained_parent_fields(&scope, id)
1093 .iter()
1094 .map(|field| field.id().unwrap())
1095 .collect::<Vec<_>>()
1096 };
1097
1098 expect_that!(find_fields("Parent"), elements_are![]);
1099 expect_that!(find_fields("Child"), elements_are![eq("b"), eq("c")]);
1100 expect_that!(find_fields("GrandChild"), elements_are![eq("c")]);
1101 expect_that!(find_fields("GrandGrandChild"), elements_are![]);
1102 Ok(())
1103 }
1104
1105 /// Create a unit test for the given PDL `code`.
1106 ///
1107 /// The unit test will compare the generated Rust code for all
1108 /// declarations with previously saved snapshots. The snapshots
1109 /// are read from `"tests/generated/{name}_{endianness}_{id}.rs"`
1110 /// where `is` taken from the declaration.
1111 ///
1112 /// When adding new tests or modifying existing ones, use
1113 /// `UPDATE_SNAPSHOTS=1 cargo test` to automatically populate the
1114 /// snapshots with the expected output.
1115 ///
1116 /// The `code` cannot have an endianness declaration, instead you
1117 /// must supply either `little_endian` or `big_endian` as
1118 /// `endianness`.
1119 macro_rules! make_pdl_test {
1120 ($name:ident, $code:expr, $endianness:ident) => {
1121 paste! {
1122 #[test]
1123 fn [< test_ $name _ $endianness >]() {
1124 let name = stringify!($name);
1125 let endianness = stringify!($endianness);
1126 let code = format!("{endianness}_packets\n{}", $code);
1127 let mut db = ast::SourceDatabase::new();
1128 let file = parse_inline(&mut db, "test", code).unwrap();
1129 let file = analyzer::analyze(&file).unwrap();
1130 let actual_code = generate(&db, &file);
1131 assert_snapshot_eq(
1132 &format!("tests/generated/rust_legacy/{name}_{endianness}.rs"),
1133 &format_rust(&actual_code),
1134 );
1135 }
1136 }
1137 };
1138 }
1139
1140 /// Create little- and bit-endian tests for the given PDL `code`.
1141 ///
1142 /// The `code` cannot have an endianness declaration: we will
1143 /// automatically generate unit tests for both
1144 /// "little_endian_packets" and "big_endian_packets".
1145 macro_rules! test_pdl {
1146 ($name:ident, $code:expr $(,)?) => {
1147 make_pdl_test!($name, $code, little_endian);
1148 make_pdl_test!($name, $code, big_endian);
1149 };
1150 }
1151
1152 test_pdl!(packet_decl_empty, "packet Foo {}");
1153
1154 test_pdl!(packet_decl_8bit_scalar, " packet Foo { x: 8 }");
1155 test_pdl!(packet_decl_24bit_scalar, "packet Foo { x: 24 }");
1156 test_pdl!(packet_decl_64bit_scalar, "packet Foo { x: 64 }");
1157
1158 test_pdl!(
1159 enum_declaration,
1160 r#"
1161 enum IncompleteTruncatedClosed : 3 {
1162 A = 0,
1163 B = 1,
1164 }
1165
1166 enum IncompleteTruncatedOpen : 3 {
1167 A = 0,
1168 B = 1,
1169 UNKNOWN = ..
1170 }
1171
1172 enum IncompleteTruncatedClosedWithRange : 3 {
1173 A = 0,
1174 B = 1..6 {
1175 X = 1,
1176 Y = 2,
1177 }
1178 }
1179
1180 enum IncompleteTruncatedOpenWithRange : 3 {
1181 A = 0,
1182 B = 1..6 {
1183 X = 1,
1184 Y = 2,
1185 },
1186 UNKNOWN = ..
1187 }
1188
1189 enum CompleteTruncated : 3 {
1190 A = 0,
1191 B = 1,
1192 C = 2,
1193 D = 3,
1194 E = 4,
1195 F = 5,
1196 G = 6,
1197 H = 7,
1198 }
1199
1200 enum CompleteTruncatedWithRange : 3 {
1201 A = 0,
1202 B = 1..7 {
1203 X = 1,
1204 Y = 2,
1205 }
1206 }
1207
1208 enum CompleteWithRange : 8 {
1209 A = 0,
1210 B = 1,
1211 C = 2..255,
1212 }
1213 "#
1214 );
1215
1216 test_pdl!(
1217 custom_field_declaration,
1218 r#"
1219 // Still unsupported.
1220 // custom_field Dynamic "dynamic"
1221
1222 // Should generate a type with From<u32> implementation.
1223 custom_field ExactSize : 32 "exact_size"
1224
1225 // Should generate a type with TryFrom<u32> implementation.
1226 custom_field TruncatedSize : 24 "truncated_size"
1227 "#
1228 );
1229
1230 test_pdl!(
1231 packet_decl_simple_scalars,
1232 r#"
1233 packet Foo {
1234 x: 8,
1235 y: 16,
1236 z: 24,
1237 }
1238 "#
1239 );
1240
1241 test_pdl!(
1242 packet_decl_complex_scalars,
1243 r#"
1244 packet Foo {
1245 a: 3,
1246 b: 8,
1247 c: 5,
1248 d: 24,
1249 e: 12,
1250 f: 4,
1251 }
1252 "#,
1253 );
1254
1255 // Test that we correctly mask a byte-sized value in the middle of
1256 // a chunk.
1257 test_pdl!(
1258 packet_decl_mask_scalar_value,
1259 r#"
1260 packet Foo {
1261 a: 2,
1262 b: 24,
1263 c: 6,
1264 }
1265 "#,
1266 );
1267
1268 test_pdl!(
1269 struct_decl_complex_scalars,
1270 r#"
1271 struct Foo {
1272 a: 3,
1273 b: 8,
1274 c: 5,
1275 d: 24,
1276 e: 12,
1277 f: 4,
1278 }
1279 "#,
1280 );
1281
1282 test_pdl!(packet_decl_8bit_enum, " enum Foo : 8 { A = 1, B = 2 } packet Bar { x: Foo }");
1283 test_pdl!(packet_decl_24bit_enum, "enum Foo : 24 { A = 1, B = 2 } packet Bar { x: Foo }");
1284 test_pdl!(packet_decl_64bit_enum, "enum Foo : 64 { A = 1, B = 2 } packet Bar { x: Foo }");
1285
1286 test_pdl!(
1287 packet_decl_mixed_scalars_enums,
1288 "
1289 enum Enum7 : 7 {
1290 A = 1,
1291 B = 2,
1292 }
1293
1294 enum Enum9 : 9 {
1295 A = 1,
1296 B = 2,
1297 }
1298
1299 packet Foo {
1300 x: Enum7,
1301 y: 5,
1302 z: Enum9,
1303 w: 3,
1304 }
1305 "
1306 );
1307
1308 test_pdl!(packet_decl_8bit_scalar_array, " packet Foo { x: 8[3] }");
1309 test_pdl!(packet_decl_24bit_scalar_array, "packet Foo { x: 24[5] }");
1310 test_pdl!(packet_decl_64bit_scalar_array, "packet Foo { x: 64[7] }");
1311
1312 test_pdl!(
1313 packet_decl_8bit_enum_array,
1314 "enum Foo : 8 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[3] }"
1315 );
1316 test_pdl!(
1317 packet_decl_24bit_enum_array,
1318 "enum Foo : 24 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[5] }"
1319 );
1320 test_pdl!(
1321 packet_decl_64bit_enum_array,
1322 "enum Foo : 64 { FOO_BAR = 1, BAZ = 2 } packet Bar { x: Foo[7] }"
1323 );
1324
1325 test_pdl!(
1326 packet_decl_array_dynamic_count,
1327 "
1328 packet Foo {
1329 _count_(x): 5,
1330 padding: 3,
1331 x: 24[]
1332 }
1333 "
1334 );
1335
1336 test_pdl!(
1337 packet_decl_array_dynamic_size,
1338 "
1339 packet Foo {
1340 _size_(x): 5,
1341 padding: 3,
1342 x: 24[]
1343 }
1344 "
1345 );
1346
1347 test_pdl!(
1348 packet_decl_array_unknown_element_width_dynamic_size,
1349 "
1350 struct Foo {
1351 _count_(a): 40,
1352 a: 16[],
1353 }
1354
1355 packet Bar {
1356 _size_(x): 40,
1357 x: Foo[],
1358 }
1359 "
1360 );
1361
1362 test_pdl!(
1363 packet_decl_array_unknown_element_width_dynamic_count,
1364 "
1365 struct Foo {
1366 _count_(a): 40,
1367 a: 16[],
1368 }
1369
1370 packet Bar {
1371 _count_(x): 40,
1372 x: Foo[],
1373 }
1374 "
1375 );
1376
1377 test_pdl!(
1378 packet_decl_array_with_padding,
1379 "
1380 struct Foo {
1381 _count_(a): 40,
1382 a: 16[],
1383 }
1384
1385 packet Bar {
1386 a: Foo[],
1387 _padding_ [128],
1388 }
1389 "
1390 );
1391
1392 test_pdl!(
1393 packet_decl_reserved_field,
1394 "
1395 packet Foo {
1396 _reserved_: 40,
1397 }
1398 "
1399 );
1400
1401 test_pdl!(
1402 packet_decl_custom_field,
1403 r#"
1404 custom_field Bar1 : 24 "exact"
1405 custom_field Bar2 : 32 "truncated"
1406
1407 packet Foo {
1408 a: Bar1,
1409 b: Bar2,
1410 }
1411 "#
1412 );
1413
1414 test_pdl!(
1415 packet_decl_fixed_scalar_field,
1416 "
1417 packet Foo {
1418 _fixed_ = 7 : 7,
1419 b: 57,
1420 }
1421 "
1422 );
1423
1424 test_pdl!(
1425 packet_decl_fixed_enum_field,
1426 "
1427 enum Enum7 : 7 {
1428 A = 1,
1429 B = 2,
1430 }
1431
1432 packet Foo {
1433 _fixed_ = A : Enum7,
1434 b: 57,
1435 }
1436 "
1437 );
1438
1439 test_pdl!(
1440 packet_decl_payload_field_variable_size,
1441 "
1442 packet Foo {
1443 a: 8,
1444 _size_(_payload_): 8,
1445 _payload_,
1446 b: 16,
1447 }
1448 "
1449 );
1450
1451 test_pdl!(
1452 packet_decl_payload_field_unknown_size,
1453 "
1454 packet Foo {
1455 a: 24,
1456 _payload_,
1457 }
1458 "
1459 );
1460
1461 test_pdl!(
1462 packet_decl_payload_field_unknown_size_terminal,
1463 "
1464 packet Foo {
1465 _payload_,
1466 a: 24,
1467 }
1468 "
1469 );
1470
1471 test_pdl!(
1472 packet_decl_child_packets,
1473 "
1474 enum Enum16 : 16 {
1475 A = 1,
1476 B = 2,
1477 }
1478
1479 packet Foo {
1480 a: 8,
1481 b: Enum16,
1482 _size_(_payload_): 8,
1483 _payload_
1484 }
1485
1486 packet Bar : Foo (a = 100) {
1487 x: 8,
1488 }
1489
1490 packet Baz : Foo (b = B) {
1491 y: 16,
1492 }
1493 "
1494 );
1495
1496 test_pdl!(
1497 packet_decl_grand_children,
1498 "
1499 enum Enum16 : 16 {
1500 A = 1,
1501 B = 2,
1502 }
1503
1504 packet Parent {
1505 foo: Enum16,
1506 bar: Enum16,
1507 baz: Enum16,
1508 _size_(_payload_): 8,
1509 _payload_
1510 }
1511
1512 packet Child : Parent (foo = A) {
1513 quux: Enum16,
1514 _payload_,
1515 }
1516
1517 packet GrandChild : Child (bar = A, quux = A) {
1518 _body_,
1519 }
1520
1521 packet GrandGrandChild : GrandChild (baz = A) {
1522 _body_,
1523 }
1524 "
1525 );
1526
1527 test_pdl!(
1528 packet_decl_parent_with_no_payload,
1529 "
1530 enum Enum8 : 8 {
1531 A = 0,
1532 }
1533
1534 packet Parent {
1535 v : Enum8,
1536 }
1537
1538 packet Child : Parent (v = A) {
1539 }
1540 "
1541 );
1542
1543 test_pdl!(
1544 packet_decl_parent_with_alias_child,
1545 "
1546 enum Enum8 : 8 {
1547 A = 0,
1548 B = 1,
1549 C = 2,
1550 }
1551
1552 packet Parent {
1553 v : Enum8,
1554 _payload_,
1555 }
1556
1557 packet AliasChild : Parent {
1558 _payload_
1559 }
1560
1561 packet NormalChild : Parent (v = A) {
1562 }
1563
1564 packet NormalGrandChild1 : AliasChild (v = B) {
1565 }
1566
1567 packet NormalGrandChild2 : AliasChild (v = C) {
1568 _payload_
1569 }
1570 "
1571 );
1572
1573 test_pdl!(
1574 reserved_identifier,
1575 "
1576 packet Test {
1577 type: 8,
1578 }
1579 "
1580 );
1581
1582 test_pdl!(
1583 payload_with_size_modifier,
1584 "
1585 packet Test {
1586 _size_(_payload_): 8,
1587 _payload_ : [+1],
1588 }
1589 "
1590 );
1591
1592 // TODO(mgeisler): enable this test when we have an approach to
1593 // struct fields with parents.
1594 //
1595 // test_pdl!(
1596 // struct_decl_child_structs,
1597 // "
1598 // enum Enum16 : 16 {
1599 // A = 1,
1600 // B = 2,
1601 // }
1602 //
1603 // struct Foo {
1604 // a: 8,
1605 // b: Enum16,
1606 // _size_(_payload_): 8,
1607 // _payload_
1608 // }
1609 //
1610 // struct Bar : Foo (a = 100) {
1611 // x: 8,
1612 // }
1613 //
1614 // struct Baz : Foo (b = B) {
1615 // y: 16,
1616 // }
1617 // "
1618 // );
1619 //
1620 // test_pdl!(
1621 // struct_decl_grand_children,
1622 // "
1623 // enum Enum16 : 16 {
1624 // A = 1,
1625 // B = 2,
1626 // }
1627 //
1628 // struct Parent {
1629 // foo: Enum16,
1630 // bar: Enum16,
1631 // baz: Enum16,
1632 // _size_(_payload_): 8,
1633 // _payload_
1634 // }
1635 //
1636 // struct Child : Parent (foo = A) {
1637 // quux: Enum16,
1638 // _payload_,
1639 // }
1640 //
1641 // struct GrandChild : Child (bar = A, quux = A) {
1642 // _body_,
1643 // }
1644 //
1645 // struct GrandGrandChild : GrandChild (baz = A) {
1646 // _body_,
1647 // }
1648 // "
1649 // );
1650 }
1651