diff options
author | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2023-07-22 04:15:52 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-07-22 04:15:52 +0000 |
commit | 7c8249c07e6897cf4323d07ee75ab18be054b7d2 (patch) | |
tree | f02381b9dac9b775be7332fe6b94c06e222d7435 | |
parent | 9655d2abccef5b7514294baf0ab4d11157f3d75a (diff) | |
parent | 0ae6528e28b344e96a3d81a5ebc5c5d9a1093a65 (diff) | |
download | pdl-compiler-7c8249c07e6897cf4323d07ee75ab18be054b7d2.tar.gz |
Merge "Revert "Revert "Update pdl-compiler to 0.1.4""" into main am: 864dc10f82 am: 8c91d72947 am: 3615d4527b am: 0ae6528e28
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/pdl-compiler/+/2671115
Change-Id: I4ae363ae1f5007680470519066e46f23390d753e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
49 files changed, 1947 insertions, 669 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 24636e7..f776014 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "6b05a0616ef2a982068e0d5fbe28d2058d49d8e0" + "sha1": "ff041c0f60f985ecc2f8c90bdec618575dcb5060" }, "path_in_vcs": "" }
\ No newline at end of file @@ -7,7 +7,7 @@ rust_binary_host { name: "generate_canonical_tests", crate_name: "generate_canonical_tests", cargo_env_compat: true, - cargo_pkg_version: "0.1.3", + cargo_pkg_version: "0.1.4", srcs: ["src/bin/generate-canonical-tests.rs"], edition: "2021", features: [ @@ -37,7 +37,7 @@ rust_library_host { name: "libpdl_compiler", crate_name: "pdl_compiler", cargo_env_compat: true, - cargo_pkg_version: "0.1.3", + cargo_pkg_version: "0.1.4", srcs: ["src/lib.rs"], edition: "2021", features: [ @@ -66,7 +66,7 @@ rust_binary_host { name: "pdlc", crate_name: "pdlc", cargo_env_compat: true, - cargo_pkg_version: "0.1.3", + cargo_pkg_version: "0.1.4", srcs: ["src/main.rs"], edition: "2021", features: [ @@ -12,7 +12,7 @@ [package] edition = "2021" name = "pdl-compiler" -version = "0.1.3" +version = "0.1.4" authors = [ "Henri Chataing <henrichataing@google.com>", "David de Jesus Duarte <licorne@google.com>", @@ -32,7 +32,7 @@ keywords = [ "grammar", ] categories = ["parsing"] -license-file = "LICENSE" +license = "Apache-2.0" repository = "https://github.com/google/pdl/" [[bin]] @@ -85,7 +85,7 @@ version = "1.2.1" features = ["serde"] [dev-dependencies.googletest] -version = "0.7.0" +version = "0.8.0" [dev-dependencies.num-derive] version = "0.3.3" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index c9fa183..cf8b0cf 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,10 +1,10 @@ [package] name = "pdl-compiler" -version = "0.1.3" +version = "0.1.4" edition = "2021" description = "Parser and serializer generator for protocol binary packets" repository = "https://github.com/google/pdl/" -license-file = "LICENSE" +license = "Apache-2.0" readme = "README.md" keywords = ["pdl", "parser", "serializer", "grammar"] authors = [ @@ -49,4 +49,4 @@ num-derive = "0.3.3" num-traits = "0.2.15" thiserror = "1.0.37" paste = "1.0.6" -googletest = "0.7.0" +googletest = "0.8.0" @@ -11,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/pdl-compiler/pdl-compiler-0.1.3.crate" + value: "https://static.crates.io/crates/pdl-compiler/pdl-compiler-0.1.4.crate" } - version: "0.1.3" + version: "0.1.4" license_type: NOTICE last_upgrade_date { year: 2023 - month: 6 - day: 27 + month: 7 + day: 13 } } diff --git a/doc/cxx-generated-code-guide.rst b/doc/cxx-generated-code-guide.rst index f694e70..3a71430 100644 --- a/doc/cxx-generated-code-guide.rst +++ b/doc/cxx-generated-code-guide.rst @@ -25,3 +25,23 @@ Example invocation: cargo run my-protocol.pdl --output-format json | \ ./scripts/generate_cxx_backend.py > my-protocol.h + +Language bindings +----------------- + +Enum declarations +^^^^^^^^^^^^^^^^^ + ++---------------------------------------+---------------------------------------------------------------+ +| :: | .. sourcecode:: c++ | +| | | +| enum TestEnum : 8 { | enum TestEnum : int8_t { | +| A = 1, | A = 1, | +| B = 2..3, | B_MIN = 2, | +| C = 4, | B_MAX = 3, | +| OTHER = .., | C = 4, | +| } | } | ++---------------------------------------+---------------------------------------------------------------+ + +.. note:: + C++ enums are open by construction, default cases in enum declarations are ignored. diff --git a/doc/python-generated-code-guide.rst b/doc/python-generated-code-guide.rst index 664d759..de766d4 100644 --- a/doc/python-generated-code-guide.rst +++ b/doc/python-generated-code-guide.rst @@ -49,9 +49,13 @@ Enum declarations | A = 1, | A = 1 | | B = 2..3, | B_MIN = 2 | | C = 4, | B_MAX = 3 | -| } | C = 4 | +| OTHER = .., | C = 4 | +| } | | +---------------------------------------+---------------------------------------------------------------+ +.. note:: + Python enums are open by construction, default cases in enum declarations are ignored. + Packet declarations ^^^^^^^^^^^^^^^^^^^ diff --git a/doc/reference.md b/doc/reference.md index 2529c29..ce2f0a7 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -161,7 +161,7 @@ A declaration is either: > enum_tag (`,` enum_tag)* `,`? > > enum_tag:\ -> enum_range | enum_value +> enum_range | enum_value | enum_other > > enum_range:\ > [IDENTIFIER](#identifier) `=` [INTEGER](#integer) `..` [INTEGER](#integer)) (`{`\ @@ -173,12 +173,20 @@ A declaration is either: > > enum_value:\ > [IDENTIFIER](#identifier) `=` [INTEGER](#integer) +> +> enum_other:\ +> [IDENTIFIER](#identifier) `=` `..` An *enumeration* or for short *enum*, is a declaration of a set of named [integer](#integer) constants or named [integer](#integer) ranges. [integer](#integer) ranges are inclusive in both ends. [integer](#integer) value within a range *must* be unique. [integer](#integer) ranges *must not* overlap. +*enumeration* are closed by default, all values that are not explicitely described in the declaration are treated as invalid and _may_ cause a parsing error. + +An *enumaration* _may_ be declared open by specifiying the default case; all unrecognized values +_shall_ falltrough to the default. + The [integer](#integer) following the name specifies the bit size of the values. ``` @@ -199,6 +207,8 @@ enum CoffeeAddition: 5 { }, Custom = 20..29, + + Other = .. } ``` diff --git a/doc/rust-generated-code-guide.rst b/doc/rust-generated-code-guide.rst index df22d92..6dfdfd0 100644 --- a/doc/rust-generated-code-guide.rst +++ b/doc/rust-generated-code-guide.rst @@ -30,6 +30,10 @@ backend may create it. impl<T> std::ops::Deref for Private<T> { .. } +.. warning:: + PDL authorizes the use of rust keywords as identifier. Keyword identifiers + are generated as raw identifiers, e.g. `type` is generated as `r#type`. + Enum declarations ^^^^^^^^^^^^^^^^^ @@ -72,3 +76,23 @@ Enum declarations | | impl From<TestEnum> for i32 { .. } | | | impl From<TestEnum> for i64 { .. } | +---------------------------------------+---------------------------------------------------------------+ +| :: | .. sourcecode:: rust | +| | | +| enum TestEnum : 8 { | #[repr(u64)] | +| A = 1, | #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] | +| B = 2, | enum TestEnum { | +| OTHER = .., | A, | +| } | B, | +| | Other(Private<u8>), | +| | } | +| | | +| | impl From<u8> for TestEnum { .. } | +| | impl From<TestEnum> for u8 { .. } | +| | impl From<TestEnum> for u16 { .. } | +| | impl From<TestEnum> for u32 { .. } | +| | impl From<TestEnum> for u64 { .. } | +| | impl From<TestEnum> for i8 { .. } | +| | impl From<TestEnum> for i16 { .. } | +| | impl From<TestEnum> for i32 { .. } | +| | impl From<TestEnum> for i64 { .. } | ++---------------------------------------+---------------------------------------------------------------+ diff --git a/scripts/generate_test_vectors.py b/scripts/generate_test_vectors.py new file mode 100755 index 0000000..ef2ea24 --- /dev/null +++ b/scripts/generate_test_vectors.py @@ -0,0 +1,685 @@ +#!/usr/bin/env python3 + +import argparse +import collections +import copy +import json +from pathlib import Path +import pprint +import traceback +from typing import Iterable, List, Optional, Union +import sys + +from pdl import ast, core + +MAX_ARRAY_SIZE = 256 +MAX_ARRAY_COUNT = 32 +DEFAULT_ARRAY_COUNT = 3 +DEFAULT_PAYLOAD_SIZE = 5 + + +class BitSerializer: + def __init__(self, big_endian: bool): + self.stream = [] + self.value = 0 + self.shift = 0 + self.byteorder = "big" if big_endian else "little" + + def append(self, value: int, width: int): + self.value = self.value | (value << self.shift) + self.shift += width + + if (self.shift % 8) == 0: + width = int(self.shift / 8) + self.stream.extend(self.value.to_bytes(width, byteorder=self.byteorder)) + self.shift = 0 + self.value = 0 + + +class Value: + def __init__(self, value: object, width: Optional[int] = None): + self.value = value + if width is not None: + self.width = width + elif isinstance(value, int) or callable(value): + raise Exception("Creating scalar value of unspecified width") + elif isinstance(value, list): + self.width = sum([v.width for v in value]) + elif isinstance(value, Packet): + self.width = value.width + else: + raise Exception(f"Malformed value {value}") + + def finalize(self, parent: "Packet"): + if callable(self.width): + self.width = self.width(parent) + + if callable(self.value): + self.value = self.value(parent) + elif isinstance(self.value, list): + for v in self.value: + v.finalize(parent) + elif isinstance(self.value, Packet): + self.value.finalize() + + def serialize_(self, serializer: BitSerializer): + if isinstance(self.value, int): + serializer.append(self.value, self.width) + elif isinstance(self.value, list): + for v in self.value: + v.serialize_(serializer) + elif isinstance(self.value, Packet): + self.value.serialize_(serializer) + else: + raise Exception(f"Malformed value {self.value}") + + def show(self, indent: int = 0): + space = " " * indent + if isinstance(self.value, int): + print(f"{space}{self.name}: {hex(self.value)}") + elif isinstance(self.value, list): + print(f"{space}{self.name}[{len(self.value)}]:") + for v in self.value: + v.show(indent + 2) + elif isinstance(self.value, Packet): + print(f"{space}{self.name}:") + self.value.show(indent + 2) + + def to_json(self) -> object: + if isinstance(self.value, int): + return self.value + elif isinstance(self.value, list): + return [v.to_json() for v in self.value] + elif isinstance(self.value, Packet): + return self.value.to_json() + + +class Field: + def __init__(self, value: Value, ref: ast.Field): + self.value = value + self.ref = ref + + def finalize(self, parent: "Packet"): + self.value.finalize(parent) + + def serialize_(self, serializer: BitSerializer): + self.value.serialize_(serializer) + + def clone(self): + return Field(copy.copy(self.value), self.ref) + + +class Packet: + def __init__(self, fields: List[Field], ref: ast.Declaration): + self.fields = fields + self.ref = ref + + def finalize(self, parent: Optional["Packet"] = None): + for f in self.fields: + f.finalize(self) + + def serialize_(self, serializer: BitSerializer): + for f in self.fields: + f.serialize_(serializer) + + def serialize(self, big_endian: bool) -> bytes: + serializer = BitSerializer(big_endian) + self.serialize_(serializer) + if serializer.shift != 0: + raise Exception("The packet size is not an integral number of octets") + return bytes(serializer.stream) + + def show(self, indent: int = 0): + for f in self.fields: + f.value.show(indent) + + def to_json(self) -> dict: + result = dict() + for f in self.fields: + if isinstance(f.ref, (ast.PayloadField, ast.BodyField)) and isinstance( + f.value.value, Packet + ): + result.update(f.value.to_json()) + elif isinstance(f.ref, (ast.PayloadField, ast.BodyField)): + result["payload"] = f.value.to_json() + elif hasattr(f.ref, "id"): + result[f.ref.id] = f.value.to_json() + return result + + @property + def width(self) -> int: + self.finalize() + return sum([f.value.width for f in self.fields]) + + +class BitGenerator: + def __init__(self): + self.value = 0 + self.shift = 0 + + def generate(self, width: int) -> Value: + """Generate an integer value of the selected width.""" + value = 0 + remains = width + while remains > 0: + w = min(8 - self.shift, remains) + mask = (1 << w) - 1 + value = (value << w) | ((self.value >> self.shift) & mask) + remains -= w + self.shift += w + if self.shift >= 8: + self.shift = 0 + self.value = (self.value + 1) % 0xFF + return Value(value, width) + + def generate_list(self, width: int, count: int) -> List[Value]: + return [self.generate(width) for n in range(count)] + + +generator = BitGenerator() + + +def generate_size_field_values(field: ast.SizeField) -> List[Value]: + def get_field_size(parent: Packet, field_id: str) -> int: + for f in parent.fields: + if ( + (field_id == "_payload_" and isinstance(f.ref, ast.PayloadField)) + or (field_id == "_body_" and isinstance(f.ref, ast.BodyField)) + or (getattr(f.ref, "id", None) == field_id) + ): + assert f.value.width % 8 == 0 + size_modifier = int(getattr(f.ref, "size_modifier", None) or 0) + return int(f.value.width / 8) + size_modifier + raise Exception( + "Field {} not found in packet {}".format(field_id, parent.ref.id) + ) + + return [Value(lambda p: get_field_size(p, field.field_id), field.width)] + + +def generate_count_field_values(field: ast.CountField) -> List[Value]: + def get_array_count(parent: Packet, field_id: str) -> int: + for f in parent.fields: + if getattr(f.ref, "id", None) == field_id: + assert isinstance(f.value.value, list) + return len(f.value.value) + raise Exception( + "Field {} not found in packet {}".format(field_id, parent.ref.id) + ) + + return [Value(lambda p: get_array_count(p, field.field_id), field.width)] + + +def generate_checksum_field_values(field: ast.TypedefField) -> List[Value]: + field_width = core.get_field_size(field) + + def basic_checksum(input: bytes, width: int): + assert width == 8 + return sum(input) % 256 + + def compute_checksum(parent: Packet, field_id: str) -> int: + serializer = None + for f in parent.fields: + if isinstance(f.ref, ast.ChecksumField) and f.ref.field_id == field_id: + serializer = BitSerializer( + f.ref.parent.file.endianness.value == "big_endian" + ) + elif isinstance(f.ref, ast.TypedefField) and f.ref.id == field_id: + return basic_checksum(serializer.stream, field_width) + elif serializer: + f.value.serialize_(serializer) + raise Exception("malformed checksum") + + return [Value(lambda p: compute_checksum(p, field.id), field_width)] + + +def generate_padding_field_values(field: ast.PaddingField) -> List[Value]: + preceding_field_id = field.padded_field.id + + def get_padding(parent: Packet, field_id: str, width: int) -> List[Value]: + for f in parent.fields: + if ( + (field_id == "_payload_" and isinstance(f.ref, ast.PayloadField)) + or (field_id == "_body_" and isinstance(f.ref, ast.BodyField)) + or (getattr(f.ref, "id", None) == field_id) + ): + assert f.value.width % 8 == 0 + assert f.value.width <= width + return width - f.value.width + raise Exception( + "Field {} not found in packet {}".format(field_id, parent.ref.id) + ) + + return [Value(0, lambda p: get_padding(p, preceding_field_id, 8 * field.size))] + + +def generate_payload_field_values( + field: Union[ast.PayloadField, ast.BodyField] +) -> List[Value]: + payload_size = core.get_payload_field_size(field) + size_modifier = int(getattr(field, "size_modifier", None) or 0) + + # If the paylaod has a size field, generate an empty payload and + # a payload of maximum size. If not generate a payload of the default size. + max_size = (1 << payload_size.width) - 1 if payload_size else DEFAULT_PAYLOAD_SIZE + max_size -= size_modifier + + assert max_size > 0 + return [Value([]), Value(generator.generate_list(8, max_size))] + + +def generate_scalar_array_field_values(field: ast.ArrayField) -> List[Value]: + if field.width % 8 != 0: + if element_width % 8 != 0: + raise Exception("Array element size is not a multiple of 8") + + array_size = core.get_array_field_size(field) + element_width = int(field.width / 8) + + # TODO + # The array might also be bounded if it is included in the sized payload + # of a packet. + + # Apply the size modifiers. + size_modifier = int(getattr(field, "size_modifier", None) or 0) + + # The element width is known, and the array element count is known + # statically. + if isinstance(array_size, int): + return [Value(generator.generate_list(field.width, array_size))] + + # The element width is known, and the array element count is known + # by count field. + elif isinstance(array_size, ast.CountField): + min_count = 0 + max_count = (1 << array_size.width) - 1 + return [Value([]), Value(generator.generate_list(field.width, max_count))] + + # The element width is known, and the array full size is known + # by size field. + elif isinstance(array_size, ast.SizeField): + min_count = 0 + max_size = (1 << array_size.width) - 1 - size_modifier + max_count = int(max_size / element_width) + return [Value([]), Value(generator.generate_list(field.width, max_count))] + + # The element width is known, but the array size is unknown. + # Generate two arrays: one empty and one including some possible element + # values. + else: + return [ + Value([]), + Value(generator.generate_list(field.width, DEFAULT_ARRAY_COUNT)), + ] + + +def generate_typedef_array_field_values(field: ast.ArrayField) -> List[Value]: + array_size = core.get_array_field_size(field) + element_width = core.get_array_element_size(field) + if element_width: + if element_width % 8 != 0: + raise Exception("Array element size is not a multiple of 8") + element_width = int(element_width / 8) + + # Generate element values to use for the generation. + type_decl = field.parent.file.typedef_scope[field.type_id] + + def generate_list(count: Optional[int]) -> List[Value]: + """Generate an array of specified length. + If the count is None all possible array items are returned.""" + element_values = generate_typedef_values(type_decl) + + # Requested a variable count, send everything in one chunk. + if count is None: + return [Value(element_values)] + # Have more items than the requested count. + # Slice the possible array values in multiple slices. + elif len(element_values) > count: + # Add more elements in case of wrap-over. + elements_count = len(element_values) + element_values.extend(generate_typedef_values(type_decl)) + chunk_count = int((len(elements) + count - 1) / count) + return [ + Value(element_values[n * count : (n + 1) * count]) + for n in range(chunk_count) + ] + # Have less items than the requested count. + # Generate additional items to fill the gap. + else: + chunk = element_values + while len(chunk) < count: + chunk.extend(generate_typedef_values(type_decl)) + return [Value(chunk[:count])] + + # TODO + # The array might also be bounded if it is included in the sized payload + # of a packet. + + # Apply the size modifier. + size_modifier = int(getattr(field, "size_modifier", None) or 0) + + min_size = 0 + max_size = MAX_ARRAY_SIZE + min_count = 0 + max_count = MAX_ARRAY_COUNT + + if field.padded_size: + max_size = field.padded_size + + if isinstance(array_size, ast.SizeField): + max_size = (1 << array_size.width) - 1 - size_modifier + min_size = size_modifier + elif isinstance(array_size, ast.CountField): + max_count = (1 << array_size.width) - 1 + elif isinstance(array_size, int): + min_count = array_size + max_count = array_size + + values = [] + chunk = [] + chunk_size = 0 + + while not values: + element_values = generate_typedef_values(type_decl) + for element_value in element_values: + element_size = int(element_value.width / 8) + + if len(chunk) >= max_count or chunk_size + element_size > max_size: + assert len(chunk) >= min_count + values.append(Value(chunk)) + chunk = [] + chunk_size = 0 + + chunk.append(element_value) + chunk_size += element_size + + if min_count == 0: + values.append(Value([])) + + return values + + # The element width is not known, but the array full octet size + # is known by size field. Generate two arrays: of minimal and maximum + # size. All unused element values are packed into arrays of varying size. + if element_width is None and isinstance(array_size, ast.SizeField): + element_values = generate_typedef_values(type_decl) + chunk = [] + chunk_size = 0 + values = [Value([])] + for element_value in element_values: + assert element_value.width % 8 == 0 + element_size = int(element_value.width / 8) + if chunk_size + element_size > max_size: + values.append(Value(chunk)) + chunk = [] + chunk.append(element_value) + chunk_size += element_size + if chunk: + values.append(Value(chunk)) + return values + + # The element width is not known, but the array element count + # is known statically or by count field. Generate two arrays: + # of minimal and maximum length. All unused element values are packed into + # arrays of varying count. + elif element_width is None and isinstance(array_size, ast.CountField): + return [Value([])] + generate_list(max_count) + + # The element width is not known, and the array element count is known + # statically. + elif element_width is None and isinstance(array_size, int): + return generate_list(array_size) + + # Neither the count not size is known, + # generate two arrays: one empty and one including all possible element + # values. + elif element_width is None: + return [Value([])] + generate_list(None) + + # The element width is known, and the array element count is known + # statically. + elif isinstance(array_size, int): + return generate_list(array_size) + + # The element width is known, and the array element count is known + # by count field. + elif isinstance(array_size, ast.CountField): + return [Value([])] + generate_list(max_count) + + # The element width is known, and the array full size is known + # by size field. + elif isinstance(array_size, ast.SizeField): + return [Value([])] + generate_list(max_count) + + # The element width is known, but the array size is unknown. + # Generate two arrays: one empty and one including all possible element + # values. + else: + return [Value([])] + generate_list(None) + + +def generate_array_field_values(field: ast.ArrayField) -> List[Value]: + if field.width is not None: + return generate_scalar_array_field_values(field) + else: + return generate_typedef_array_field_values(field) + + +def generate_typedef_field_values( + field: ast.TypedefField, constraints: List[ast.Constraint] +) -> List[Value]: + type_decl = field.parent.file.typedef_scope[field.type_id] + + # Check for constraint on enum field. + if isinstance(type_decl, ast.EnumDeclaration): + for c in constraints: + if c.id == field.id: + for tag in type_decl.tags: + if tag.id == c.tag_id: + return [Value(tag.value, type_decl.width)] + raise Exception("undefined enum tag") + + # Checksum field needs to known the checksum range. + if isinstance(type_decl, ast.ChecksumDeclaration): + return generate_checksum_field_values(field) + + return generate_typedef_values(type_decl) + + +def generate_field_values( + field: ast.Field, constraints: List[ast.Constraint], payload: Optional[List[Packet]] +) -> List[Value]: + if isinstance(field, ast.ChecksumField): + # Checksum fields are just markers. + return [Value(0, 0)] + + elif isinstance(field, ast.PaddingField): + return generate_padding_field_values(field) + + elif isinstance(field, ast.SizeField): + return generate_size_field_values(field) + + elif isinstance(field, ast.CountField): + return generate_count_field_values(field) + + elif isinstance(field, (ast.BodyField, ast.PayloadField)) and payload: + return [Value(p) for p in payload] + + elif isinstance(field, (ast.BodyField, ast.PayloadField)): + return generate_payload_field_values(field) + + elif isinstance(field, ast.FixedField) and field.enum_id: + enum_decl = field.parent.file.typedef_scope[field.enum_id] + for tag in enum_decl.tags: + if tag.id == field.tag_id: + return [Value(tag.value, enum_decl.width)] + raise Exception("undefined enum tag") + + elif isinstance(field, ast.FixedField) and field.width: + return [Value(field.value, field.width)] + + elif isinstance(field, ast.ReservedField): + return [Value(0, field.width)] + + elif isinstance(field, ast.ArrayField): + return generate_array_field_values(field) + + elif isinstance(field, ast.ScalarField): + for c in constraints: + if c.id == field.id: + return [Value(c.value, field.width)] + mask = (1 << field.width) - 1 + return [ + Value(0, field.width), + Value(-1 & mask, field.width), + generator.generate(field.width), + ] + + elif isinstance(field, ast.TypedefField): + return generate_typedef_field_values(field, constraints) + + else: + raise Exception("unsupported field kind") + + +def generate_fields( + decl: ast.Declaration, + constraints: List[ast.Constraint], + payload: Optional[List[Packet]], +) -> List[List[Field]]: + return [ + [Field(v, f) for v in generate_field_values(f, constraints, payload)] + for f in decl.fields + ] + + +def generate_fields_recursive( + scope: dict, + decl: ast.Declaration, + constraints: List[ast.Constraint] = [], + payload: Optional[List[Packet]] = None, +) -> List[List[Field]]: + fields = generate_fields(decl, constraints, payload) + + if not decl.parent_id: + return fields + + packets = [Packet(fields, decl) for fields in product(fields)] + parent_decl = scope[decl.parent_id] + return generate_fields_recursive( + scope, parent_decl, constraints + decl.constraints, payload=packets + ) + + +def generate_struct_values(decl: ast.StructDeclaration) -> List[Packet]: + fields = generate_fields_recursive(decl.file.typedef_scope, decl) + return [Packet(fields, decl) for fields in product(fields)] + + +def generate_packet_values(decl: ast.PacketDeclaration) -> List[Packet]: + fields = generate_fields_recursive(decl.file.packet_scope, decl) + return [Packet(fields, decl) for fields in product(fields)] + + +def generate_typedef_values(decl: ast.Declaration) -> List[Value]: + if isinstance(decl, ast.EnumDeclaration): + return [Value(t.value, decl.width) for t in decl.tags] + + elif isinstance(decl, ast.ChecksumDeclaration): + raise Exception("ChecksumDeclaration handled in typedef field") + + elif isinstance(decl, ast.CustomFieldDeclaration): + raise Exception("TODO custom field") + + elif isinstance(decl, ast.StructDeclaration): + return [Value(p) for p in generate_struct_values(decl)] + + else: + raise Exception("unsupported typedef declaration type") + + +def product(fields: List[List[Field]]) -> List[List[Field]]: + """Perform a cartesian product of generated options for packet field values.""" + + def aux(vec: List[List[Field]]) -> List[List[Field]]: + if len(vec) == 0: + return [[]] + return [[item.clone()] + items for item in vec[0] for items in aux(vec[1:])] + + count = 1 + max_len = 0 + for f in fields: + count *= len(f) + max_len = max(max_len, len(f)) + + # Limit products to 32 elements to prevent combinatorial + # explosion. + if count <= 32: + return aux(fields) + + # If too many products, select samples which test all fields value + # values at the minimum. + else: + return [[f[idx % len(f)] for f in fields] for idx in range(0, max_len + 1)] + + +def serialize_values(file: ast.File, values: List[Value]) -> List[dict]: + results = [] + for v in values: + v.finalize() + packed = v.serialize(file.endianness.value == "big_endian") + result = { + "packed": "".join([f"{b:02x}" for b in packed]), + "unpacked": v.to_json(), + } + if v.ref.parent_id: + result["packet"] = v.ref.id + results.append(result) + return results + + +def run(input: Path, packet: List[str]): + with open(input) as f: + file = ast.File.from_json(json.load(f)) + core.desugar(file) + + results = dict() + for decl in file.packet_scope.values(): + if core.get_derived_packets(decl) or (packet and decl.id not in packet): + continue + + try: + values = generate_packet_values(decl) + ancestor = core.get_packet_ancestor(decl) + results[ancestor.id] = results.get(ancestor.id, []) + serialize_values( + file, values + ) + except Exception as exn: + print( + f"Skipping packet {decl.id}; cannot generate values: {exn}", + file=sys.stderr, + ) + + results = [{"packet": k, "tests": v} for (k, v) in results.items()] + json.dump(results, sys.stdout, indent=2) + + +def main() -> int: + """Generate test vectors for top-level PDL packets.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--input", type=Path, required=True, help="Input PDL-JSON source" + ) + parser.add_argument( + "--packet", + type=lambda x: x.split(","), + required=False, + action="extend", + default=[], + help="Select PDL packet to test", + ) + return run(**vars(parser.parse_args())) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/analyzer.rs b/src/analyzer.rs index 1757e5e..c89d3d0 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -190,6 +190,7 @@ pub enum ErrorCode { DuplicateTagRange = 41, E42 = 42, E43 = 43, + DuplicateDefaultTag = 44, } impl From<ErrorCode> for String { @@ -205,8 +206,10 @@ pub struct Diagnostics { } /// Gather information about the full AST. -#[derive(Debug, Default)] -pub struct Scope<'d, A: Annotation> { +#[derive(Debug)] +pub struct Scope<'d, A: Annotation = ast::Annotation> { + /// Reference to the source file. + pub file: &'d crate::ast::File<A>, /// Collection of Group, Packet, Enum, Struct, Checksum, and CustomField /// declarations. pub typedef: HashMap<String, &'d crate::ast::Decl<A>>, @@ -245,7 +248,7 @@ impl Diagnostics { impl<'d, A: Annotation + Default> Scope<'d, A> { pub fn new(file: &'d crate::ast::File<A>) -> Result<Scope<'d, A>, Diagnostics> { // Gather top-level declarations. - let mut scope: Scope<A> = Default::default(); + let mut scope: Scope<A> = Scope { file, typedef: Default::default() }; let mut diagnostics: Diagnostics = Default::default(); for decl in &file.declarations { if let Some(id) = decl.id() { @@ -277,6 +280,14 @@ impl<'d, A: Annotation + Default> Scope<'d, A> { } } + /// Iterate over the child declarations of the selected declaration. + pub fn iter_children<'s>( + &'s self, + decl: &'d crate::ast::Decl<A>, + ) -> impl Iterator<Item = &'d crate::ast::Decl<A>> + 's { + self.file.iter_children(decl) + } + /// Return the parent declaration of the selected declaration, /// if it has one. pub fn get_parent(&self, decl: &crate::ast::Decl<A>) -> Option<&'d crate::ast::Decl<A>> { @@ -291,6 +302,15 @@ impl<'d, A: Annotation + Default> Scope<'d, A> { std::iter::successors(self.get_parent(decl), |decl| self.get_parent(decl)) } + /// Iterate over the parent declarations of the selected declaration, + /// including the current declaration. + pub fn iter_parents_and_self<'s>( + &'s self, + decl: &'d crate::ast::Decl<A>, + ) -> impl Iterator<Item = &'d Decl<A>> + 's { + std::iter::successors(Some(decl), |decl| self.get_parent(decl)) + } + /// Iterate over the declaration and its parent's fields. pub fn iter_fields<'s>( &'s self, @@ -299,11 +319,27 @@ impl<'d, A: Annotation + Default> Scope<'d, A> { std::iter::successors(Some(decl), |decl| self.get_parent(decl)).flat_map(Decl::fields) } + /// Iterate over the declaration parent's fields. + pub fn iter_parent_fields<'s>( + &'s self, + decl: &'d crate::ast::Decl<A>, + ) -> impl Iterator<Item = &'d crate::ast::Field<A>> + 's { + std::iter::successors(self.get_parent(decl), |decl| self.get_parent(decl)) + .flat_map(Decl::fields) + } + + /// Iterate over the declaration and its parent's constraints. + pub fn iter_constraints<'s>( + &'s self, + decl: &'d crate::ast::Decl<A>, + ) -> impl Iterator<Item = &'d Constraint> + 's { + std::iter::successors(Some(decl), |decl| self.get_parent(decl)).flat_map(Decl::constraints) + } + /// Return the type declaration for the selected field, if applicable. - #[allow(dead_code)] - pub fn get_declaration( + pub fn get_type_declaration( &self, - field: &'d crate::ast::Field<A>, + field: &crate::ast::Field<A>, ) -> Option<&'d crate::ast::Decl<A>> { match &field.desc { FieldDesc::Checksum { .. } @@ -323,6 +359,24 @@ impl<'d, A: Annotation + Default> Scope<'d, A> { | FieldDesc::Typedef { type_id, .. } => self.typedef.get(type_id).cloned(), } } + + /// Test if the selected field is a bit-field. + pub fn is_bitfield(&self, field: &crate::ast::Field<A>) -> bool { + match &field.desc { + FieldDesc::Size { .. } + | FieldDesc::Count { .. } + | FieldDesc::ElementSize { .. } + | FieldDesc::FixedScalar { .. } + | FieldDesc::FixedEnum { .. } + | FieldDesc::Reserved { .. } + | FieldDesc::Scalar { .. } => true, + FieldDesc::Typedef { type_id, .. } => { + let field = self.typedef.get(type_id.as_str()); + matches!(field, Some(Decl { desc: DeclDesc::Enum { .. }, .. })) + } + _ => false, + } + } } /// Return the bit-width of a scalar value. @@ -682,6 +736,39 @@ fn check_enum_declarations(file: &parser_ast::File) -> Result<(), Diagnostics> { } } + fn check_tag_other<'a>( + tag: &'a TagOther, + tags_by_id: &mut HashMap<&'a str, SourceRange>, + tag_other: &mut Option<SourceRange>, + diagnostics: &mut Diagnostics, + ) { + if let Some(prev) = tags_by_id.insert(&tag.id, tag.loc) { + diagnostics.push( + Diagnostic::error() + .with_code(ErrorCode::DuplicateTagIdentifier) + .with_message(format!("duplicate tag identifier `{}`", tag.id)) + .with_labels(vec![ + tag.loc.primary(), + prev.secondary() + .with_message(format!("`{}` is first declared here", tag.id)), + ]), + ) + } + if let Some(prev) = tag_other { + diagnostics.push( + Diagnostic::error() + .with_code(ErrorCode::DuplicateDefaultTag) + .with_message("duplicate default tag".to_owned()) + .with_labels(vec![ + tag.loc.primary(), + prev.secondary() + .with_message("the default tag is first declared here".to_owned()), + ]), + ) + } + *tag_other = Some(tag.loc) + } + let mut diagnostics: Diagnostics = Default::default(); for decl in &file.declarations { if let DeclDesc::Enum { tags, width, .. } = &decl.desc { @@ -694,6 +781,7 @@ fn check_enum_declarations(file: &parser_ast::File) -> Result<(), Diagnostics> { _ => None, }) .collect::<Vec<_>>(); + let mut tag_other = None; for tag in tags { match tag { @@ -712,6 +800,9 @@ fn check_enum_declarations(file: &parser_ast::File) -> Result<(), Diagnostics> { &mut tags_by_value, &mut diagnostics, ), + Tag::Other(other) => { + check_tag_other(other, &mut tags_by_id, &mut tag_other, &mut diagnostics) + } } } @@ -1842,6 +1933,17 @@ mod test { } "# ); + + raises!( + DuplicateTagIdentifier, + r#" + little_endian_packets + enum A : 8 { + X = 0, + X = .., + } + "# + ); } #[test] @@ -2520,6 +2622,22 @@ mod test { } #[test] + fn test_e44() { + raises!( + DuplicateDefaultTag, + r#" + little_endian_packets + enum A : 8 { + A = 0, + X = .., + B = 1, + Y = .., + } + "# + ); + } + + #[test] fn test_enum_declaration() { valid!( r#" @@ -2554,6 +2672,17 @@ mod test { } "# ); + + valid!( + r#" + little_endian_packets + enum A : 7 { + A = 50..100, + X = 101, + UNKNOWN = .., + } + "# + ); } use analyzer::ast::Size; @@ -86,11 +86,19 @@ pub struct TagRange { pub tags: Vec<TagValue>, } +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "kind", rename = "tag")] +pub struct TagOther { + pub id: String, + pub loc: SourceRange, +} + #[derive(Debug, Serialize, Clone, PartialEq, Eq)] #[serde(untagged)] pub enum Tag { Value(TagValue), Range(TagRange), + Other(TagOther), } #[derive(Debug, Serialize, Clone)] @@ -288,23 +296,35 @@ impl PartialEq for TagRange { } } +impl Eq for TagOther {} +impl PartialEq for TagOther { + fn eq(&self, other: &Self) -> bool { + // Implement structual equality, leave out loc. + self.id == other.id + } +} + impl Tag { pub fn id(&self) -> &str { match self { - Tag::Value(TagValue { id, .. }) | Tag::Range(TagRange { id, .. }) => id, + Tag::Value(TagValue { id, .. }) + | Tag::Range(TagRange { id, .. }) + | Tag::Other(TagOther { id, .. }) => id, } } pub fn loc(&self) -> &SourceRange { match self { - Tag::Value(TagValue { loc, .. }) | Tag::Range(TagRange { loc, .. }) => loc, + Tag::Value(TagValue { loc, .. }) + | Tag::Range(TagRange { loc, .. }) + | Tag::Other(TagOther { loc, .. }) => loc, } } pub fn value(&self) -> Option<usize> { match self { Tag::Value(TagValue { value, .. }) => Some(*value), - Tag::Range(_) => None, + Tag::Range(_) | Tag::Other(_) => None, } } } @@ -451,6 +471,31 @@ impl<A: Annotation> Decl<A> { } } + /// Return the reference to the payload or body field in a declaration, + /// if present. + pub fn payload(&self) -> Option<&Field<A>> { + self.fields() + .find(|field| matches!(&field.desc, FieldDesc::Payload { .. } | FieldDesc::Body { .. })) + } + + /// Return the reference to the payload or body size field in a declaration, + /// if present. + pub fn payload_size(&self) -> Option<&Field<A>> { + self.fields().find(|field| match &field.desc { + FieldDesc::Size { field_id, .. } => field_id == "_payload_" || field_id == "_body_", + _ => false, + }) + } + + /// Return the reference to the array size or count field in a declaration, + /// if present. + pub fn array_size(&self, id: &str) -> Option<&Field<A>> { + self.fields().find(|field| match &field.desc { + FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id, + _ => false, + }) + } + pub fn kind(&self) -> &str { match &self.desc { DeclDesc::Checksum { .. } => "checksum", diff --git a/src/backends/rust.rs b/src/backends/rust.rs index f369179..9dfdf8a 100644 --- a/src/backends/rust.rs +++ b/src/backends/rust.rs @@ -14,9 +14,10 @@ //! Rust compiler backend. -use crate::{ast, lint}; +use crate::{analyzer, ast}; use quote::{format_ident, quote}; use std::collections::BTreeSet; +use std::collections::HashMap; use std::path::Path; use syn::LitInt; @@ -30,25 +31,25 @@ mod types; use parser::FieldParser; use serializer::FieldSerializer; -#[cfg(not(tm_mainline_prod))] pub use heck::ToUpperCamelCase; -#[cfg(tm_mainline_prod)] -pub trait ToUpperCamelCase { - fn to_upper_camel_case(&self) -> String; +pub trait ToIdent { + /// Generate a sanitized rust identifier. + /// Rust specific keywords are renamed for validity. + fn to_ident(self) -> proc_macro2::Ident; } -#[cfg(tm_mainline_prod)] -impl ToUpperCamelCase for str { - fn to_upper_camel_case(&self) -> String { - use heck::CamelCase; - let camel_case = self.to_camel_case(); - if camel_case.is_empty() { - camel_case - } else { - // PDL identifiers are a-zA-z0-9, so we're dealing with - // simple ASCII text. - format!("{}{}", &camel_case[..1].to_ascii_uppercase(), &camel_case[1..]) +impl ToIdent for &'_ str { + fn to_ident(self) -> proc_macro2::Ident { + match self { + "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern" + | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" + | "move" | "mut" | "pub" | "ref" | "return" | "self" | "Self" | "static" | "struct" + | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while" + | "async" | "await" | "dyn" | "abstract" | "become" | "box" | "do" | "final" + | "macro" | "override" | "priv" | "typeof" | "unsized" | "virtual" | "yield" + | "try" => format_ident!("r#{}", self), + _ => format_ident!("{}", self), } } } @@ -84,7 +85,7 @@ pub fn mask_bits(n: usize, suffix: &str) -> syn::LitInt { } fn generate_packet_size_getter<'a>( - scope: &lint::Scope<'a>, + scope: &analyzer::Scope<'a>, fields: impl Iterator<Item = &'a analyzer_ast::Field>, is_packet: bool, ) -> (usize, proc_macro2::TokenStream) { @@ -97,7 +98,7 @@ fn generate_packet_size_getter<'a>( continue; } - let decl = scope.get_field_declaration(field); + let decl = scope.get_type_declaration(field); dynamic_widths.push(match &field.desc { ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body { .. } => { if is_packet { @@ -111,11 +112,11 @@ fn generate_packet_size_getter<'a>( } } ast::FieldDesc::Typedef { id, .. } => { - let id = format_ident!("{id}"); + let id = id.to_ident(); quote!(self.#id.get_size()) } ast::FieldDesc::Array { id, width, .. } => { - let id = format_ident!("{id}"); + let id = id.to_ident(); match &decl { Some(analyzer_ast::Decl { desc: ast::DeclDesc::Struct { .. } | ast::DeclDesc::CustomField { .. }, @@ -163,7 +164,10 @@ fn generate_packet_size_getter<'a>( ) } -fn top_level_packet<'a>(scope: &lint::Scope<'a>, packet_name: &'a str) -> &'a analyzer_ast::Decl { +fn top_level_packet<'a>( + scope: &analyzer::Scope<'a>, + packet_name: &'a str, +) -> &'a analyzer_ast::Decl { let mut decl = scope.typedef[packet_name]; while let ast::DeclDesc::Packet { parent_id: Some(parent_id), .. } | ast::DeclDesc::Struct { parent_id: Some(parent_id), .. } = &decl.desc @@ -173,74 +177,68 @@ fn top_level_packet<'a>(scope: &lint::Scope<'a>, packet_name: &'a str) -> &'a an decl } -/// Find all constrained fields in children of `id`. -fn find_constrained_fields<'a>( - scope: &'a lint::Scope<'a>, - id: &'a str, +/// Find parent fields which are constrained in child packets. +/// +/// These fields are the fields which need to be passed in when +/// parsing a `id` packet since their values are needed for one or +/// more child packets. +fn find_constrained_parent_fields<'a>( + scope: &analyzer::Scope<'a>, + id: &str, ) -> Vec<&'a analyzer_ast::Field> { + let all_parent_fields: HashMap<String, &'a analyzer_ast::Field> = HashMap::from_iter( + scope + .iter_parent_fields(scope.typedef[id]) + .filter_map(|f| f.id().map(|id| (id.to_string(), f))), + ); + let mut fields = Vec::new(); let mut field_names = BTreeSet::new(); - let mut children = scope.iter_children(id).collect::<Vec<_>>(); + let mut children = scope.iter_children(scope.typedef[id]).collect::<Vec<_>>(); while let Some(child) = children.pop() { if let ast::DeclDesc::Packet { id, constraints, .. } | ast::DeclDesc::Struct { id, constraints, .. } = &child.desc { - let packet_scope = &scope.scopes[&scope.typedef[id]]; for constraint in constraints { - if field_names.insert(&constraint.id) { - fields.push(packet_scope.all_fields[&constraint.id]); + if field_names.insert(&constraint.id) + && all_parent_fields.contains_key(&constraint.id) + { + fields.push(all_parent_fields[&constraint.id]); } } - children.extend(scope.iter_children(id).collect::<Vec<_>>()); + children.extend(scope.iter_children(scope.typedef[id]).collect::<Vec<_>>()); } } fields } -/// Find parent fields which are constrained in child packets. -/// -/// These fields are the fields which need to be passed in when -/// parsing a `id` packet since their values are needed for one or -/// more child packets. -fn find_constrained_parent_fields<'a>( - scope: &'a lint::Scope<'a>, - id: &'a str, -) -> impl Iterator<Item = &'a analyzer_ast::Field> { - let packet_scope = &scope.scopes[&scope.typedef[id]]; - find_constrained_fields(scope, id).into_iter().filter(|field| { - let id = field.id().unwrap(); - packet_scope.all_fields.contains_key(id) && packet_scope.get_packet_field(id).is_none() - }) -} - /// Generate the declaration and implementation for a data struct. /// /// This struct will hold the data for a packet or a struct. It knows /// how to parse and serialize its own fields. fn generate_data_struct( - scope: &lint::Scope<'_>, + scope: &analyzer::Scope<'_>, endianness: ast::EndiannessValue, id: &str, ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { let decl = scope.typedef[id]; - let packet_scope = &scope.scopes[&decl]; let is_packet = matches!(&decl.desc, ast::DeclDesc::Packet { .. }); let span = format_ident!("bytes"); let serializer_span = format_ident!("buffer"); let mut field_parser = FieldParser::new(scope, endianness, id, &span); let mut field_serializer = FieldSerializer::new(scope, endianness, id, &serializer_span); - for field in packet_scope.iter_fields() { + for field in decl.fields() { field_parser.add(field); field_serializer.add(field); } field_parser.done(); let (parse_arg_names, parse_arg_types) = if is_packet { - let fields = find_constrained_parent_fields(scope, id).collect::<Vec<_>>(); - let names = fields.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>(); + let fields = find_constrained_parent_fields(scope, id); + let names = fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>(); let types = fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>(); (names, types) } else { @@ -248,7 +246,7 @@ fn generate_data_struct( }; let (constant_width, packet_size) = - generate_packet_size_getter(scope, packet_scope.iter_fields(), is_packet); + generate_packet_size_getter(scope, decl.fields(), is_packet); let conforms = if constant_width == 0 { quote! { true } } else { @@ -257,14 +255,13 @@ fn generate_data_struct( }; let visibility = if is_packet { quote!() } else { quote!(pub) }; - let has_payload = packet_scope.get_payload_field().is_some(); - let has_children = scope.iter_children(id).next().is_some(); + let has_payload = decl.payload().is_some(); + let has_children = scope.iter_children(decl).next().is_some(); - let struct_name = if is_packet { format_ident!("{id}Data") } else { format_ident!("{id}") }; - let fields_with_ids = - packet_scope.iter_fields().filter(|f| f.id().is_some()).collect::<Vec<_>>(); + let struct_name = if is_packet { format_ident!("{id}Data") } else { id.to_ident() }; + let fields_with_ids = decl.fields().filter(|f| f.id().is_some()).collect::<Vec<_>>(); let mut field_names = - fields_with_ids.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>(); + fields_with_ids.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>(); let mut field_types = fields_with_ids.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>(); if has_children || has_payload { if is_packet { @@ -326,26 +323,10 @@ fn generate_data_struct( (data_struct_decl, data_struct_impl) } -/// Find all parents from `id`. -/// -/// This includes the `Decl` for `id` itself. -fn find_parents<'a>(scope: &lint::Scope<'a>, id: &str) -> Vec<&'a analyzer_ast::Decl> { - let mut decl = scope.typedef[id]; - let mut parents = vec![decl]; - while let ast::DeclDesc::Packet { parent_id: Some(parent_id), .. } - | ast::DeclDesc::Struct { parent_id: Some(parent_id), .. } = &decl.desc - { - decl = scope.typedef[parent_id]; - parents.push(decl); - } - parents.reverse(); - parents -} - /// Turn the constraint into a value (such as `10` or /// `SomeEnum::Foo`). pub fn constraint_to_value( - packet_scope: &lint::PacketScope<'_>, + all_fields: &HashMap<String, &'_ analyzer_ast::Field>, constraint: &ast::Constraint, ) -> proc_macro2::TokenStream { match constraint { @@ -356,8 +337,8 @@ pub fn constraint_to_value( // TODO(mgeisler): include type_id in `ast::Constraint` and // drop the packet_scope argument. ast::Constraint { tag_id: Some(tag_id), .. } => { - let type_id = match &packet_scope.all_fields[&constraint.id].desc { - ast::FieldDesc::Typedef { type_id, .. } => format_ident!("{type_id}"), + let type_id = match &all_fields[&constraint.id].desc { + ast::FieldDesc::Typedef { type_id, .. } => type_id.to_ident(), _ => unreachable!("Invalid constraint: {constraint:?}"), }; let tag_id = format_ident!("{}", tag_id.to_upper_camel_case()); @@ -369,95 +350,99 @@ pub fn constraint_to_value( /// Generate code for a `ast::Decl::Packet`. fn generate_packet_decl( - scope: &lint::Scope<'_>, + scope: &analyzer::Scope<'_>, endianness: ast::EndiannessValue, id: &str, ) -> proc_macro2::TokenStream { - let packet_scope = &scope.scopes[&scope.typedef[id]]; - + let decl = scope.typedef[id]; let top_level = top_level_packet(scope, id); let top_level_id = top_level.id().unwrap(); - let top_level_packet = format_ident!("{top_level_id}"); + let top_level_packet = top_level_id.to_ident(); let top_level_data = format_ident!("{top_level_id}Data"); - let top_level_id_lower = format_ident!("{}", top_level_id.to_lowercase()); + let top_level_id_lower = top_level_id.to_lowercase().to_ident(); // TODO(mgeisler): use the convert_case crate to convert between // `FooBar` and `foo_bar` in the code below. let span = format_ident!("bytes"); - let id_lower = format_ident!("{}", id.to_lowercase()); - let id_packet = format_ident!("{id}"); + let id_lower = id.to_lowercase().to_ident(); + let id_packet = id.to_ident(); let id_child = format_ident!("{id}Child"); let id_data_child = format_ident!("{id}DataChild"); let id_builder = format_ident!("{id}Builder"); - let parents = find_parents(scope, id); + let mut parents = scope.iter_parents_and_self(decl).collect::<Vec<_>>(); + parents.reverse(); + let parent_ids = parents.iter().map(|p| p.id().unwrap()).collect::<Vec<_>>(); - let parent_shifted_ids = parent_ids.iter().skip(1).map(|id| format_ident!("{id}")); + let parent_shifted_ids = parent_ids.iter().skip(1).map(|id| id.to_ident()); let parent_lower_ids = - parent_ids.iter().map(|id| format_ident!("{}", id.to_lowercase())).collect::<Vec<_>>(); + parent_ids.iter().map(|id| id.to_lowercase().to_ident()).collect::<Vec<_>>(); let parent_shifted_lower_ids = parent_lower_ids.iter().skip(1).collect::<Vec<_>>(); - let parent_packet = parent_ids.iter().map(|id| format_ident!("{id}")); + let parent_packet = parent_ids.iter().map(|id| id.to_ident()); let parent_data = parent_ids.iter().map(|id| format_ident!("{id}Data")); let parent_data_child = parent_ids.iter().map(|id| format_ident!("{id}DataChild")); let all_fields = { - let mut fields = packet_scope.all_fields.values().collect::<Vec<_>>(); + let mut fields = scope.iter_fields(decl).filter(|d| d.id().is_some()).collect::<Vec<_>>(); fields.sort_by_key(|f| f.id()); fields }; - let all_field_names = - all_fields.iter().map(|f| format_ident!("{}", f.id().unwrap())).collect::<Vec<_>>(); + let all_named_fields = + HashMap::from_iter(all_fields.iter().map(|f| (f.id().unwrap().to_string(), *f))); + + let all_field_names = all_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>(); let all_field_types = all_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>(); let all_field_borrows = all_fields.iter().map(|f| types::rust_borrow(f, scope)).collect::<Vec<_>>(); - let all_field_getter_names = all_field_names.iter().map(|id| format_ident!("get_{id}")); + let all_field_getter_names = + all_fields.iter().map(|f| format_ident!("get_{}", f.id().unwrap())); let all_field_self_field = all_fields.iter().map(|f| { for (parent, parent_id) in parents.iter().zip(parent_lower_ids.iter()) { - if scope.scopes[parent].iter_fields().any(|ff| ff.id() == f.id()) { + if parent.fields().any(|ff| ff.id() == f.id()) { return quote!(self.#parent_id); } } unreachable!("Could not find {f:?} in parent chain"); }); + let all_constraints = HashMap::<String, _>::from_iter( + scope.iter_constraints(decl).map(|c| (c.id.to_string(), c)), + ); + let unconstrained_fields = all_fields .iter() - .filter(|f| !packet_scope.all_constraints.contains_key(f.id().unwrap())) - .collect::<Vec<_>>(); - let unconstrained_field_names = unconstrained_fields - .iter() - .map(|f| format_ident!("{}", f.id().unwrap())) + .filter(|f| !all_constraints.contains_key(f.id().unwrap())) .collect::<Vec<_>>(); + let unconstrained_field_names = + unconstrained_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>(); let unconstrained_field_types = unconstrained_fields.iter().map(|f| types::rust_type(f)); let rev_parents = parents.iter().rev().collect::<Vec<_>>(); let builder_assignments = rev_parents.iter().enumerate().map(|(idx, parent)| { let parent_id = parent.id().unwrap(); - let parent_id_lower = format_ident!("{}", parent_id.to_lowercase()); + let parent_id_lower = parent_id.to_lowercase().to_ident(); let parent_data = format_ident!("{parent_id}Data"); let parent_data_child = format_ident!("{parent_id}DataChild"); - let parent_packet_scope = &scope.scopes[&scope.typedef[parent_id]]; let named_fields = { - let mut names = - parent_packet_scope.iter_fields().filter_map(ast::Field::id).collect::<Vec<_>>(); + let mut names = parent.fields().filter_map(ast::Field::id).collect::<Vec<_>>(); names.sort_unstable(); names }; - let mut field = named_fields.iter().map(|id| format_ident!("{id}")).collect::<Vec<_>>(); + let mut field = named_fields.iter().map(|id| id.to_ident()).collect::<Vec<_>>(); let mut value = named_fields .iter() - .map(|&id| match packet_scope.all_constraints.get(id) { - Some(constraint) => constraint_to_value(packet_scope, constraint), + .map(|&id| match all_constraints.get(id) { + Some(constraint) => constraint_to_value(&all_named_fields, constraint), None => { - let id = format_ident!("{id}"); + let id = id.to_ident(); quote!(self.#id) } }) .collect::<Vec<_>>(); - if parent_packet_scope.get_payload_field().is_some() { + if parent.payload().is_some() { field.push(format_ident!("child")); if idx == 0 { // Top-most parent, the child is simply created from @@ -471,13 +456,13 @@ fn generate_packet_decl( } else { // Child is created from the previous parent. let prev_parent_id = rev_parents[idx - 1].id().unwrap(); - let prev_parent_id_lower = format_ident!("{}", prev_parent_id.to_lowercase()); - let prev_parent_id = format_ident!("{prev_parent_id}"); + let prev_parent_id_lower = prev_parent_id.to_lowercase().to_ident(); + let prev_parent_id = prev_parent_id.to_ident(); value.push(quote! { #parent_data_child::#prev_parent_id(#prev_parent_id_lower) }); } - } else if scope.iter_children(parent_id).next().is_some() { + } else if scope.iter_children(parent).next().is_some() { field.push(format_ident!("child")); value.push(quote! { #parent_data_child::None }); } @@ -489,11 +474,10 @@ fn generate_packet_decl( } }); - let children = scope.iter_children(id).collect::<Vec<_>>(); - let has_payload = packet_scope.get_payload_field().is_some(); + let children = scope.iter_children(decl).collect::<Vec<_>>(); + let has_payload = decl.payload().is_some(); let has_children_or_payload = !children.is_empty() || has_payload; - let child = - children.iter().map(|child| format_ident!("{}", child.id().unwrap())).collect::<Vec<_>>(); + let child = children.iter().map(|child| child.id().unwrap().to_ident()).collect::<Vec<_>>(); let child_data = child.iter().map(|child| format_ident!("{child}Data")).collect::<Vec<_>>(); let get_payload = (children.is_empty() && has_payload).then(|| { quote! { @@ -555,8 +539,7 @@ fn generate_packet_decl( } }); - let ancestor_packets = - parent_ids[..parent_ids.len() - 1].iter().map(|id| format_ident!("{id}")); + let ancestor_packets = parent_ids[..parent_ids.len() - 1].iter().map(|id| id.to_ident()); let impl_from_and_try_from = (top_level_id != id).then(|| { quote! { #( @@ -689,7 +672,7 @@ fn generate_packet_decl( /// Generate code for a `ast::Decl::Struct`. fn generate_struct_decl( - scope: &lint::Scope<'_>, + scope: &analyzer::Scope<'_>, endianness: ast::EndiannessValue, id: &str, ) -> proc_macro2::TokenStream { @@ -710,20 +693,26 @@ fn generate_struct_decl( /// an additional Unknown case for unmatched valued. Complete /// enums (where the full range of values is covered) are /// automatically closed. -fn generate_enum_decl( - id: &str, - tags: &[ast::Tag], - width: usize, - open: bool, -) -> proc_macro2::TokenStream { +fn generate_enum_decl(id: &str, tags: &[ast::Tag], width: usize) -> proc_macro2::TokenStream { + // Determine if the enum is open, i.e. a default tag is defined. + fn enum_default_tag(tags: &[ast::Tag]) -> Option<ast::TagOther> { + tags.iter() + .filter_map(|tag| match tag { + ast::Tag::Other(tag) => Some(tag.clone()), + _ => None, + }) + .next() + } + // Determine if the enum is complete, i.e. all values in the backing // integer range have a matching tag in the original declaration. fn enum_is_complete(tags: &[ast::Tag], max: usize) -> bool { let mut ranges = tags .iter() - .map(|tag| match tag { - ast::Tag::Value(tag) => (tag.value, tag.value), - ast::Tag::Range(tag) => tag.range.clone().into_inner(), + .filter_map(|tag| match tag { + ast::Tag::Value(tag) => Some((tag.value, tag.value)), + ast::Tag::Range(tag) => Some(tag.range.clone().into_inner()), + _ => None, }) .collect::<Vec<_>>(); ranges.sort_unstable(); @@ -738,8 +727,7 @@ fn generate_enum_decl( }) } - // Determine if the enum is primitive, i.e. does not contain any - // tag range. + // Determine if the enum is primitive, i.e. does not contain any tag range. fn enum_is_primitive(tags: &[ast::Tag]) -> bool { tags.iter().all(|tag| matches!(tag, ast::Tag::Value(_))) } @@ -768,13 +756,15 @@ fn generate_enum_decl( let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let range_max = scalar_max(width); + let default_tag = enum_default_tag(tags); + let is_open = default_tag.is_some(); let is_complete = enum_is_complete(tags, scalar_max(width)); let is_primitive = enum_is_primitive(tags); - let name = format_ident!("{id}"); + let name = id.to_ident(); // Generate the variant cases for the enum declaration. // Tags declared in ranges are flattened in the same declaration. - let use_variant_values = is_primitive && (is_complete || !open); + let use_variant_values = is_primitive && (is_complete || !is_open); let repr_u64 = use_variant_values.then(|| quote! { #[repr(u64)] }); let mut variants = vec![]; for tag in tags.iter() { @@ -790,6 +780,7 @@ fn generate_enum_decl( let id = format_tag_ident(&tag.id); variants.push(quote! { #id(Private<#backing_type>) }) } + ast::Tag::Other(_) => (), } } @@ -813,6 +804,7 @@ fn generate_enum_decl( let end = format_value(*tag.range.end()); from_cases.push(quote! { #start ..= #end => Ok(#name::#id(Private(value))) }) } + ast::Tag::Other(_) => (), } } @@ -834,19 +826,22 @@ fn generate_enum_decl( let id = format_tag_ident(&tag.id); into_cases.push(quote! { #name::#id(Private(value)) => *value }) } + ast::Tag::Other(_) => (), } } // Generate a default case if the enum is open and incomplete. - if !is_complete && open { - variants.push(quote! { Unknown(Private<#backing_type>) }); - from_cases.push(quote! { 0..#range_max => Ok(#name::Unknown(Private(value))) }); - into_cases.push(quote! { #name::Unknown(Private(value)) => *value }); + if !is_complete && is_open { + let unknown_id = format_tag_ident(&default_tag.unwrap().id); + let range_max = format_value(range_max); + variants.push(quote! { #unknown_id(Private<#backing_type>) }); + from_cases.push(quote! { 0..=#range_max => Ok(#name::#unknown_id(Private(value))) }); + into_cases.push(quote! { #name::#unknown_id(Private(value)) => *value }); } // Generate an error case if the enum size is lower than the backing // type size, or if the enum is closed or incomplete. - if backing_type.width != width || (!is_complete && !open) { + if backing_type.width != width || (!is_complete && !is_open) { from_cases.push(quote! { _ => Err(value) }); } @@ -907,7 +902,7 @@ fn generate_enum_decl( /// * `id` - Enum identifier. /// * `width` - Width of the backing type of the enum, in bits. fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream { - let id = format_ident!("{}", id); + let id = id.to_ident(); let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let max_value = mask_bits(width, &format!("u{}", backing_type.width)); @@ -964,7 +959,7 @@ fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStrea } fn generate_decl( - scope: &lint::Scope<'_>, + scope: &analyzer::Scope<'_>, file: &analyzer_ast::File, decl: &analyzer_ast::Decl, ) -> proc_macro2::TokenStream { @@ -978,7 +973,7 @@ fn generate_decl( // implement the recursive (de)serialization. generate_struct_decl(scope, file.endianness.value, id) } - ast::DeclDesc::Enum { id, tags, width } => generate_enum_decl(id, tags, *width, false), + ast::DeclDesc::Enum { id, tags, width } => generate_enum_decl(id, tags, *width), ast::DeclDesc::CustomField { id, width: Some(width), .. } => { generate_custom_field_decl(id, *width) } @@ -994,7 +989,7 @@ pub fn generate(sources: &ast::SourceDatabase, file: &analyzer_ast::File) -> Str let source = sources.get(file.file).expect("could not read source"); let preamble = preamble::generate(Path::new(source.name())); - let scope = lint::Scope::new(file); + let scope = analyzer::Scope::new(file).expect("could not create scope"); let decls = file.declarations.iter().map(|decl| generate_decl(&scope, file, decl)); let code = quote! { #preamble @@ -1050,9 +1045,10 @@ mod tests { } "; let file = parse_str(code); - let scope = lint::Scope::new(&file); + let scope = analyzer::Scope::new(&file).unwrap(); let find_fields = |id| { find_constrained_parent_fields(&scope, id) + .iter() .map(|field| field.id().unwrap()) .collect::<Vec<_>>() }; @@ -1120,14 +1116,18 @@ mod tests { test_pdl!( enum_declaration, r#" - // Should generate unknown case. - enum IncompleteTruncated : 3 { + enum IncompleteTruncatedClosed : 3 { + A = 0, + B = 1, + } + + enum IncompleteTruncatedOpen : 3 { A = 0, B = 1, + UNKNOWN = .. } - // Should generate unknown case. - enum IncompleteTruncatedWithRange : 3 { + enum IncompleteTruncatedClosedWithRange : 3 { A = 0, B = 1..6 { X = 1, @@ -1135,7 +1135,15 @@ mod tests { } } - // Should generate unreachable case. + enum IncompleteTruncatedOpenWithRange : 3 { + A = 0, + B = 1..6 { + X = 1, + Y = 2, + }, + UNKNOWN = .. + } + enum CompleteTruncated : 3 { A = 0, B = 1, @@ -1147,7 +1155,6 @@ mod tests { H = 7, } - // Should generate unreachable case. enum CompleteTruncatedWithRange : 3 { A = 0, B = 1..7 { @@ -1156,7 +1163,6 @@ mod tests { } } - // Should generate no unknown or unreachable case. enum CompleteWithRange : 8 { A = 0, B = 1, @@ -1522,6 +1528,15 @@ mod tests { " ); + test_pdl!( + reserved_identifier, + " + packet Test { + type: 8, + } + " + ); + // TODO(mgeisler): enable this test when we have an approach to // struct fields with parents. // diff --git a/src/backends/rust/parser.rs b/src/backends/rust/parser.rs index 203a19b..f837082 100644 --- a/src/backends/rust/parser.rs +++ b/src/backends/rust/parser.rs @@ -14,11 +14,12 @@ use crate::analyzer::ast as analyzer_ast; use crate::backends::rust::{ - constraint_to_value, find_constrained_parent_fields, mask_bits, types, ToUpperCamelCase, + constraint_to_value, find_constrained_parent_fields, mask_bits, types, ToIdent, + ToUpperCamelCase, }; -use crate::{ast, lint}; +use crate::{analyzer, ast}; use quote::{format_ident, quote}; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; fn size_field_ident(id: &str) -> proc_macro2::Ident { format_ident!("{}_size", id.trim_matches('_')) @@ -31,8 +32,9 @@ struct BitField<'a> { } pub struct FieldParser<'a> { - scope: &'a lint::Scope<'a>, + scope: &'a analyzer::Scope<'a>, endianness: ast::EndiannessValue, + decl: &'a analyzer_ast::Decl, packet_name: &'a str, span: &'a proc_macro2::Ident, chunk: Vec<BitField<'a>>, @@ -43,7 +45,7 @@ pub struct FieldParser<'a> { impl<'a> FieldParser<'a> { pub fn new( - scope: &'a lint::Scope<'a>, + scope: &'a analyzer::Scope<'a>, endianness: ast::EndiannessValue, packet_name: &'a str, span: &'a proc_macro2::Ident, @@ -51,6 +53,7 @@ impl<'a> FieldParser<'a> { FieldParser { scope, endianness, + decl: scope.typedef[packet_name], packet_name, span, chunk: Vec::new(), @@ -70,7 +73,7 @@ impl<'a> FieldParser<'a> { type_id.as_deref(), *size, field.annot.padded_size, - self.scope.get_field_declaration(field), + self.scope.get_type_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => self.add_typedef_field(id, type_id), ast::FieldDesc::Payload { size_modifier, .. } => { @@ -139,19 +142,20 @@ impl<'a> FieldParser<'a> { self.code.push(match &field.desc { ast::FieldDesc::Scalar { id, .. } => { - let id = format_ident!("{id}"); + let id = id.to_ident(); quote! { let #id = #v; } } ast::FieldDesc::FixedEnum { enum_id, tag_id, .. } => { - let enum_id = format_ident!("{enum_id}"); - let tag_id = format_ident!("{}", tag_id.to_upper_camel_case()); + let enum_id = enum_id.to_ident(); + let tag_id = tag_id.to_upper_camel_case().to_ident(); quote! { - if #v != #value_type::from(#enum_id::#tag_id) { + let fixed_value = #v; + if fixed_value != #value_type::from(#enum_id::#tag_id) { return Err(Error::InvalidFixedValue { expected: #value_type::from(#enum_id::#tag_id) as u64, - actual: #v as u64, + actual: fixed_value as u64, }); } } @@ -159,10 +163,11 @@ impl<'a> FieldParser<'a> { ast::FieldDesc::FixedScalar { value, .. } => { let value = proc_macro2::Literal::usize_unsuffixed(*value); quote! { + let fixed_value = #v; if #v != #value { return Err(Error::InvalidFixedValue { expected: #value, - actual: #v as u64, + actual: fixed_value as u64, }); } } @@ -171,13 +176,13 @@ impl<'a> FieldParser<'a> { let field_name = id; let type_name = type_id; let packet_name = &self.packet_name; - let id = format_ident!("{id}"); - let type_id = format_ident!("{type_id}"); + let id = id.to_ident(); + let type_id = type_id.to_ident(); quote! { - let #id = #type_id::try_from(#v).map_err(|_| Error::InvalidEnumValueError { + let #id = #type_id::try_from(#v).map_err(|unknown_val| Error::InvalidEnumValueError { obj: #packet_name.to_string(), field: #field_name.to_string(), - value: #v as u64, + value: unknown_val as u64, type_: #type_name.to_string(), })?; } @@ -216,27 +221,23 @@ impl<'a> FieldParser<'a> { self.shift = 0; } - fn packet_scope(&self) -> Option<&lint::PacketScope> { - self.scope.scopes.get(self.scope.typedef.get(self.packet_name)?) - } - fn find_count_field(&self, id: &str) -> Option<proc_macro2::Ident> { - match self.packet_scope()?.get_array_size_field(id)?.desc { + match self.decl.array_size(id)?.desc { ast::FieldDesc::Count { .. } => Some(format_ident!("{id}_count")), _ => None, } } fn find_size_field(&self, id: &str) -> Option<proc_macro2::Ident> { - match self.packet_scope()?.get_array_size_field(id)?.desc { + match self.decl.array_size(id)?.desc { ast::FieldDesc::Size { .. } => Some(size_field_ident(id)), _ => None, } } fn payload_field_offset_from_end(&self) -> Option<usize> { - let packet_scope = self.packet_scope().unwrap(); - let mut fields = packet_scope.iter_fields(); + let decl = self.scope.typedef[self.packet_name]; + let mut fields = decl.fields(); fields.find(|f| { matches!(f.desc, ast::FieldDesc::Body { .. } | ast::FieldDesc::Payload { .. }) })?; @@ -327,7 +328,7 @@ impl<'a> FieldParser<'a> { None => self.span.clone(), }; - let id = format_ident!("{id}"); + let id = id.to_ident(); let parse_element = self.parse_array_element(&span, width, type_id, decl); match (element_width, &array_shape) { @@ -468,8 +469,8 @@ impl<'a> FieldParser<'a> { } let span = self.span; - let id = format_ident!("{id}"); - let type_id = format_ident!("{type_id}"); + let id = id.to_ident(); + let type_id = type_id.to_ident(); self.code.push(match decl.annot.size { analyzer_ast::Size::Unknown | analyzer_ast::Size::Dynamic => quote! { @@ -510,8 +511,7 @@ impl<'a> FieldParser<'a> { /// Parse body and payload fields. fn add_payload_field(&mut self, size_modifier: Option<&str>) { let span = self.span; - let packet_scope = self.packet_scope().unwrap(); - let payload_size_field = packet_scope.get_payload_size_field(); + let payload_size_field = self.decl.payload_size(); let offset_from_end = self.payload_field_offset_from_end(); if size_modifier.is_some() { @@ -597,19 +597,19 @@ impl<'a> FieldParser<'a> { if let Some(ast::DeclDesc::Enum { id, width, .. }) = decl.map(|decl| &decl.desc) { let get_uint = types::get_uint(self.endianness, *width, span); - let type_id = format_ident!("{id}"); + let type_id = id.to_ident(); let packet_name = &self.packet_name; return quote! { - #type_id::try_from(#get_uint).map_err(|_| Error::InvalidEnumValueError { + #type_id::try_from(#get_uint).map_err(|unknown_val| Error::InvalidEnumValueError { obj: #packet_name.to_string(), field: String::new(), // TODO(mgeisler): fill out or remove - value: 0, + value: unknown_val as u64, type_: #id.to_string(), }) }; } - let type_id = format_ident!("{}", type_id.unwrap()); + let type_id = type_id.unwrap().to_ident(); quote! { #type_id::parse_inner(#span) } @@ -621,12 +621,15 @@ impl<'a> FieldParser<'a> { return; // Structs don't parse the child structs recursively. } - let packet_scope = &self.scope.scopes[&decl]; - let children = self.scope.iter_children(self.packet_name).collect::<Vec<_>>(); - if children.is_empty() && packet_scope.get_payload_field().is_none() { + let children = self.scope.iter_children(decl).collect::<Vec<_>>(); + if children.is_empty() && self.decl.payload().is_none() { return; } + let all_fields = HashMap::<String, _>::from_iter( + self.scope.iter_fields(decl).filter_map(|f| f.id().map(|id| (id.to_string(), f))), + ); + // Gather fields that are constrained in immediate child declarations. // Keep the fields sorted by name. // TODO: fields that are only matched in grand children will not be included. @@ -639,10 +642,11 @@ impl<'a> FieldParser<'a> { let mut child_parse_args = Vec::new(); let mut child_ids_data = Vec::new(); let mut child_ids = Vec::new(); + let get_constraint_value = |mut constraints: std::slice::Iter<'_, ast::Constraint>, id: &str| -> Option<proc_macro2::TokenStream> { - constraints.find(|c| c.id == id).map(|c| constraint_to_value(packet_scope, c)) + constraints.find(|c| c.id == id).map(|c| constraint_to_value(&all_fields, c)) }; for child in children.iter() { @@ -673,16 +677,17 @@ impl<'a> FieldParser<'a> { .collect::<Vec<_>>(); let fields = find_constrained_parent_fields(self.scope, child.id().unwrap()) - .map(|field| format_ident!("{}", field.id().unwrap())); + .iter() + .map(|field| field.id().unwrap().to_ident()) + .collect::<Vec<_>>(); match_values.push(quote!( (#(#tuple_values),*) )); child_parse_args.push(quote!( #(, #fields)*)); child_ids_data.push(format_ident!("{}Data", child.id().unwrap())); - child_ids.push(format_ident!("{}", child.id().unwrap())); + child_ids.push(child.id().unwrap().to_ident()); } - let constrained_field_idents = - constrained_fields.iter().map(|field| format_ident!("{field}")); + let constrained_field_idents = constrained_fields.iter().map(|field| field.to_ident()); let packet_data_child = format_ident!("{}DataChild", self.packet_name); // Parsing of packet children requires having a payload field; @@ -750,7 +755,7 @@ mod tests { } "; let file = parse_str(code); - let scope = lint::Scope::new(&file); + let scope = analyzer::Scope::new(&file).unwrap(); let span = format_ident!("bytes"); let parser = FieldParser::new(&scope, file.endianness.value, "P", &span); assert_eq!(parser.find_size_field("a"), None); @@ -767,7 +772,7 @@ mod tests { } "; let file = parse_str(code); - let scope = lint::Scope::new(&file); + let scope = analyzer::Scope::new(&file).unwrap(); let span = format_ident!("bytes"); let parser = FieldParser::new(&scope, file.endianness.value, "P", &span); assert_eq!(parser.find_size_field("b"), None); @@ -784,7 +789,7 @@ mod tests { } "; let file = parse_str(code); - let scope = lint::Scope::new(&file); + let scope = analyzer::Scope::new(&file).unwrap(); let span = format_ident!("bytes"); let parser = FieldParser::new(&scope, file.endianness.value, "P", &span); assert_eq!(parser.find_size_field("c"), Some(format_ident!("c_size"))); diff --git a/src/backends/rust/serializer.rs b/src/backends/rust/serializer.rs index 497936c..345e98a 100644 --- a/src/backends/rust/serializer.rs +++ b/src/backends/rust/serializer.rs @@ -13,8 +13,8 @@ // limitations under the License. use crate::analyzer::ast as analyzer_ast; -use crate::backends::rust::{mask_bits, types, ToUpperCamelCase}; -use crate::{ast, lint}; +use crate::backends::rust::{mask_bits, types, ToIdent, ToUpperCamelCase}; +use crate::{analyzer, ast}; use quote::{format_ident, quote}; /// A single bit-field value. @@ -25,7 +25,7 @@ struct BitField { } pub struct FieldSerializer<'a> { - scope: &'a lint::Scope<'a>, + scope: &'a analyzer::Scope<'a>, endianness: ast::EndiannessValue, packet_name: &'a str, span: &'a proc_macro2::Ident, @@ -36,7 +36,7 @@ pub struct FieldSerializer<'a> { impl<'a> FieldSerializer<'a> { pub fn new( - scope: &'a lint::Scope<'a>, + scope: &'a analyzer::Scope<'a>, endianness: ast::EndiannessValue, packet_name: &'a str, span: &'a proc_macro2::Ident, @@ -59,7 +59,7 @@ impl<'a> FieldSerializer<'a> { id, *width, field.annot.padded_size, - self.scope.get_field_declaration(field), + self.scope.get_type_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => { self.add_typedef_field(id, type_id); @@ -79,7 +79,7 @@ impl<'a> FieldSerializer<'a> { match &field.desc { ast::FieldDesc::Scalar { id, width } => { - let field_name = format_ident!("{id}"); + let field_name = id.to_ident(); let field_type = types::Integer::new(*width); if field_type.width > *width { let packet_name = &self.packet_name; @@ -97,7 +97,7 @@ impl<'a> FieldSerializer<'a> { } ast::FieldDesc::FixedEnum { enum_id, tag_id, .. } => { let field_type = types::Integer::new(width); - let enum_id = format_ident!("{enum_id}"); + let enum_id = enum_id.to_ident(); let tag_id = format_ident!("{}", tag_id.to_upper_camel_case()); self.chunk.push(BitField { value: quote!(#field_type::from(#enum_id::#tag_id)), @@ -111,7 +111,7 @@ impl<'a> FieldSerializer<'a> { self.chunk.push(BitField { value: quote!(#value), field_type, shift }); } ast::FieldDesc::Typedef { id, .. } => { - let field_name = format_ident!("{id}"); + let field_name = id.to_ident(); let field_type = types::Integer::new(width); self.chunk.push(BitField { value: quote!(#field_type::from(self.#field_name)), @@ -127,14 +127,21 @@ impl<'a> FieldSerializer<'a> { let max_value = mask_bits(*width, "usize"); let decl = self.scope.typedef.get(self.packet_name).unwrap(); - let scope = self.scope.scopes.get(decl).unwrap(); - let value_field = scope.get_packet_field(field_id).unwrap(); + let value_field = self + .scope + .iter_fields(decl) + .find(|field| match &field.desc { + ast::FieldDesc::Payload { .. } => field_id == "_payload_", + ast::FieldDesc::Body { .. } => field_id == "_body_", + _ => field.id() == Some(field_id), + }) + .unwrap(); - let field_name = format_ident!("{field_id}"); + let field_name = field_id.to_ident(); let field_type = types::Integer::new(*width); // TODO: size modifier - let value_field_decl = self.scope.get_field_declaration(value_field); + let value_field_decl = self.scope.get_type_declaration(value_field); let field_size_name = format_ident!("{field_id}_size"); let array_size = match (&value_field.desc, value_field_decl.map(|decl| &decl.desc)) @@ -183,7 +190,7 @@ impl<'a> FieldSerializer<'a> { }); } ast::FieldDesc::Count { field_id, width, .. } => { - let field_name = format_ident!("{field_id}"); + let field_name = field_id.to_ident(); let field_type = types::Integer::new(*width); if field_type.width > *width { let packet_name = &self.packet_name; @@ -289,7 +296,7 @@ impl<'a> FieldSerializer<'a> { } }; - let id = format_ident!("{id}"); + let id = id.to_ident(); self.code.push(match padding_size { Some(padding_size) => @@ -320,7 +327,7 @@ impl<'a> FieldSerializer<'a> { panic!("Derived struct used in typedef field"); } - let id = format_ident!("{id}"); + let id = id.to_ident(); let span = format_ident!("{}", self.span); self.code.push(match &decl.desc { @@ -354,8 +361,8 @@ impl<'a> FieldSerializer<'a> { let child_ids = self .scope - .iter_children(self.packet_name) - .map(|child| format_ident!("{}", child.id().unwrap())) + .iter_children(decl) + .map(|child| child.id().unwrap().to_ident()) .collect::<Vec<_>>(); let span = format_ident!("{}", self.span); diff --git a/src/backends/rust/types.rs b/src/backends/rust/types.rs index 5b1767d..b5f2b91 100644 --- a/src/backends/rust/types.rs +++ b/src/backends/rust/types.rs @@ -15,7 +15,8 @@ //! Utility functions for dealing with Rust integer types. use crate::analyzer::ast as analyzer_ast; -use crate::{ast, lint}; +use crate::backends::rust::ToIdent; +use crate::{analyzer, ast}; use quote::{format_ident, quote}; /// A Rust integer type such as `u8`. @@ -55,7 +56,7 @@ pub fn rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream { quote!(#field_type) } ast::FieldDesc::Typedef { type_id, .. } => { - let field_type = format_ident!("{type_id}"); + let field_type = type_id.to_ident(); quote!(#field_type) } ast::FieldDesc::Array { width: Some(width), size: Some(size), .. } => { @@ -68,12 +69,12 @@ pub fn rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream { quote!(Vec<#field_type>) } ast::FieldDesc::Array { type_id: Some(type_id), size: Some(size), .. } => { - let field_type = format_ident!("{type_id}"); + let field_type = type_id.to_ident(); let size = proc_macro2::Literal::usize_unsuffixed(*size); quote!([#field_type; #size]) } ast::FieldDesc::Array { type_id: Some(type_id), size: None, .. } => { - let field_type = format_ident!("{type_id}"); + let field_type = type_id.to_ident(); quote!(Vec<#field_type>) } //ast::Field::Size { .. } | ast::Field::Count { .. } => quote!(), @@ -83,7 +84,7 @@ pub fn rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream { pub fn rust_borrow( field: &analyzer_ast::Field, - scope: &lint::Scope<'_>, + scope: &analyzer::Scope<'_>, ) -> proc_macro2::TokenStream { match &field.desc { ast::FieldDesc::Scalar { .. } => quote!(), @@ -17,7 +17,6 @@ pub mod analyzer; pub mod ast; pub mod backends; -pub mod lint; pub mod parser; #[cfg(test)] pub mod test_utils; diff --git a/src/lint.rs b/src/lint.rs deleted file mode 100644 index 421f364..0000000 --- a/src/lint.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::HashMap; - -use crate::analyzer::ast as analyzer_ast; -use crate::ast::*; - -/// Gather information about the full AST. -#[derive(Debug)] -pub struct Scope<'d> { - // Original file. - pub file: &'d analyzer_ast::File, - - // Collection of Group, Packet, Enum, Struct, Checksum, and CustomField declarations. - pub typedef: HashMap<String, &'d analyzer_ast::Decl>, - - // Collection of Packet, Struct, and Group scope declarations. - pub scopes: HashMap<&'d analyzer_ast::Decl, PacketScope<'d>>, -} - -/// Gather information about a Packet, Struct, or Group declaration. -#[derive(Debug)] -pub struct PacketScope<'d> { - // Original decl. - decl: &'d analyzer_ast::Decl, - - // Local and inherited field declarations. Only named fields are preserved. - // Saved here for reference for parent constraint resolving. - pub all_fields: HashMap<String, &'d analyzer_ast::Field>, - - // Local and inherited constraint declarations. - // Saved here for constraint conflict checks. - pub all_constraints: HashMap<String, &'d Constraint>, -} - -impl<'d> std::hash::Hash for &'d analyzer_ast::Decl { - fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - std::ptr::hash(*self, state); - } -} - -impl<'d> PacketScope<'d> { - /// Add parent fields and constraints to the scope. - /// Only named fields are imported. - fn inherit( - &mut self, - parent: &PacketScope<'d>, - constraints: impl Iterator<Item = &'d Constraint>, - ) { - // Check constraints. - assert!(self.all_constraints.is_empty()); - self.all_constraints = parent.all_constraints.clone(); - for constraint in constraints { - let id = constraint.id.clone(); - self.all_constraints.insert(id, constraint); - } - - // Save parent fields. - self.all_fields = parent.all_fields.clone(); - } - - /// Iterate over the packet's fields. - pub fn iter_fields(&self) -> impl Iterator<Item = &'d analyzer_ast::Field> { - self.decl.fields() - } - - /// Lookup a field by name. This will also find the special - /// `_payload_` and `_body_` fields. - pub fn get_packet_field(&self, id: &str) -> Option<&analyzer_ast::Field> { - self.decl.fields().find(|field| match &field.desc { - FieldDesc::Payload { .. } => id == "_payload_", - FieldDesc::Body { .. } => id == "_body_", - _ => field.id() == Some(id), - }) - } - - /// Find the payload or body field, if any. - pub fn get_payload_field(&self) -> Option<&analyzer_ast::Field> { - self.decl - .fields() - .find(|field| matches!(&field.desc, FieldDesc::Payload { .. } | FieldDesc::Body { .. })) - } - - /// Lookup the size field for an array field. - pub fn get_array_size_field(&self, id: &str) -> Option<&analyzer_ast::Field> { - self.decl.fields().find(|field| match &field.desc { - FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id, - _ => false, - }) - } - - /// Find the size field corresponding to the payload or body - /// field of this packet. - pub fn get_payload_size_field(&self) -> Option<&analyzer_ast::Field> { - self.decl.fields().find(|field| match &field.desc { - FieldDesc::Size { field_id, .. } => field_id == "_payload_" || field_id == "_body_", - _ => false, - }) - } - - /// Cleanup scope after processing all fields. - fn finalize(&mut self) { - // Check field shadowing. - for f in self.decl.fields() { - if let Some(id) = f.id() { - self.all_fields.insert(id.to_string(), f); - } - } - } -} - -impl<'d> Scope<'d> { - pub fn new(file: &analyzer_ast::File) -> Scope<'_> { - let mut scope = Scope { file, typedef: HashMap::new(), scopes: HashMap::new() }; - - // Gather top-level declarations. - // Validate the top-level scopes (Group, Packet, Typedef). - // - // TODO: switch to try_insert when stable - for decl in &file.declarations { - if let Some(id) = decl.id() { - scope.typedef.insert(id.to_string(), decl); - } - } - - scope.finalize(); - scope - } - - // Sort Packet, Struct, and Group declarations by reverse topological - // order. - fn finalize(&mut self) -> Vec<&'d analyzer_ast::Decl> { - // Auxiliary function implementing BFS on Packet tree. - enum Mark { - Temporary, - Permanent, - } - struct Context<'d> { - list: Vec<&'d analyzer_ast::Decl>, - visited: HashMap<&'d analyzer_ast::Decl, Mark>, - scopes: HashMap<&'d analyzer_ast::Decl, PacketScope<'d>>, - } - - fn bfs<'s, 'd>( - decl: &'d analyzer_ast::Decl, - context: &'s mut Context<'d>, - scope: &Scope<'d>, - ) -> Option<&'s PacketScope<'d>> { - match context.visited.get(&decl) { - Some(Mark::Permanent) => return context.scopes.get(&decl), - Some(Mark::Temporary) => { - return None; - } - _ => (), - } - - let (parent_id, fields) = match &decl.desc { - DeclDesc::Packet { parent_id, fields, .. } - | DeclDesc::Struct { parent_id, fields, .. } => (parent_id.as_ref(), fields), - DeclDesc::Group { fields, .. } => (None, fields), - _ => return None, - }; - - context.visited.insert(decl, Mark::Temporary); - let mut lscope = - PacketScope { decl, all_fields: HashMap::new(), all_constraints: HashMap::new() }; - - // Iterate over Struct and Group fields. - for f in fields { - match &f.desc { - FieldDesc::Group { .. } => unreachable!(), - FieldDesc::Typedef { type_id, .. } => match scope.typedef.get(type_id) { - Some(struct_decl @ Decl { desc: DeclDesc::Struct { .. }, .. }) => { - bfs(struct_decl, context, scope); - } - None | Some(_) => (), - }, - _ => (), - } - } - - // Iterate over parent declaration. - let parent = parent_id.and_then(|id| scope.typedef.get(id)); - if let Some(parent_decl) = parent { - if let Some(rscope) = bfs(parent_decl, context, scope) { - // Import the parent fields and constraints into the current scope. - lscope.inherit(rscope, decl.constraints()) - } - } - - lscope.finalize(); - context.list.push(decl); - context.visited.insert(decl, Mark::Permanent); - context.scopes.insert(decl, lscope); - context.scopes.get(&decl) - } - - let mut context = - Context::<'d> { list: vec![], visited: HashMap::new(), scopes: HashMap::new() }; - - for decl in self.typedef.values() { - bfs(decl, &mut context, self); - } - - self.scopes = context.scopes; - context.list - } - - pub fn iter_children<'a>( - &'a self, - id: &'a str, - ) -> impl Iterator<Item = &'d analyzer_ast::Decl> + 'a { - self.file.iter_children(self.typedef.get(id).unwrap()) - } - - /// Return the declaration of the typedef type backing the - /// selected field. - pub fn get_field_declaration( - &self, - field: &analyzer_ast::Field, - ) -> Option<&'d analyzer_ast::Decl> { - match &field.desc { - FieldDesc::FixedEnum { enum_id, .. } => self.typedef.get(enum_id).copied(), - FieldDesc::Array { type_id: Some(type_id), .. } => self.typedef.get(type_id).copied(), - FieldDesc::Typedef { type_id, .. } => self.typedef.get(type_id.as_str()).copied(), - _ => None, - } - } - - /// Test if the selected field is a bitfield. - pub fn is_bitfield(&self, field: &analyzer_ast::Field) -> bool { - match &field.desc { - FieldDesc::Size { .. } - | FieldDesc::Count { .. } - | FieldDesc::ElementSize { .. } - | FieldDesc::FixedScalar { .. } - | FieldDesc::FixedEnum { .. } - | FieldDesc::Reserved { .. } - | FieldDesc::Scalar { .. } => true, - FieldDesc::Typedef { type_id, .. } => { - let field = self.typedef.get(type_id.as_str()); - matches!(field, Some(Decl { desc: DeclDesc::Enum { .. }, .. })) - } - _ => false, - } - } -} diff --git a/src/parser.rs b/src/parser.rs index 6d19648..f9d2ffa 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -69,7 +69,8 @@ enum_range = { enum_value_list ~ "}")? } -enum_tag = { enum_range | enum_value } +enum_other = { identifier ~ "=" ~ ".." } +enum_tag = { enum_range | enum_value | enum_other } enum_tag_list = { enum_tag ~ ("," ~ enum_tag)* ~ ","? } enum_declaration = { "enum" ~ identifier ~ ":" ~ integer ~ "{" ~ @@ -350,6 +351,17 @@ fn parse_enum_range(node: Node<'_>, context: &Context) -> Result<crate::ast::Tag } } +fn parse_enum_other(node: Node<'_>, context: &Context) -> Result<crate::ast::TagOther, String> { + if node.as_rule() != Rule::enum_other { + err_unexpected_rule(Rule::enum_other, node.as_rule()) + } else { + let loc = node.as_loc(context); + let mut children = node.children(); + let id = parse_identifier(&mut children)?; + Ok(crate::ast::TagOther { id, loc }) + } +} + fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<crate::ast::Tag, String> { if node.as_rule() != Rule::enum_tag { err_unexpected_rule(Rule::enum_tag, node.as_rule()) @@ -361,6 +373,9 @@ fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<crate::ast::Tag, Some(node) if node.as_rule() == Rule::enum_range => { Ok(crate::ast::Tag::Range(parse_enum_range(node, context)?)) } + Some(node) if node.as_rule() == Rule::enum_other => { + Ok(crate::ast::Tag::Other(parse_enum_other(node, context)?)) + } Some(node) => Err(format!( "expected rule {:?} or {:?}, got {:?}", Rule::enum_value, diff --git a/tests/generated/enum_declaration_big_endian.rs b/tests/generated/enum_declaration_big_endian.rs index badb3f0..ffc4725 100644 --- a/tests/generated/enum_declaration_big_endian.rs +++ b/tests/generated/enum_declaration_big_endian.rs @@ -47,136 +47,278 @@ pub trait Packet { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] -pub enum IncompleteTruncated { +pub enum IncompleteTruncatedClosed { A = 0x0, B = 0x1, } -impl TryFrom<u8> for IncompleteTruncated { +impl TryFrom<u8> for IncompleteTruncatedClosed { type Error = u8; fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { match value { - 0x0 => Ok(IncompleteTruncated::A), - 0x1 => Ok(IncompleteTruncated::B), + 0x0 => Ok(IncompleteTruncatedClosed::A), + 0x1 => Ok(IncompleteTruncatedClosed::B), _ => Err(value), } } } -impl From<&IncompleteTruncated> for u8 { - fn from(value: &IncompleteTruncated) -> Self { +impl From<&IncompleteTruncatedClosed> for u8 { + fn from(value: &IncompleteTruncatedClosed) -> Self { match value { - IncompleteTruncated::A => 0x0, - IncompleteTruncated::B => 0x1, + IncompleteTruncatedClosed::A => 0x0, + IncompleteTruncatedClosed::B => 0x1, } } } -impl From<IncompleteTruncated> for u8 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u8 { + fn from(value: IncompleteTruncatedClosed) -> Self { (&value).into() } } -impl From<IncompleteTruncated> for i8 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i8 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i16 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i16 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i32 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i32 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i64 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i64 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u16 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u16 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u32 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u32 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u64 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u64 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] -pub enum IncompleteTruncatedWithRange { +pub enum IncompleteTruncatedOpen { + A, + B, + Unknown(Private<u8>), +} +impl TryFrom<u8> for IncompleteTruncatedOpen { + type Error = u8; + fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { + match value { + 0x0 => Ok(IncompleteTruncatedOpen::A), + 0x1 => Ok(IncompleteTruncatedOpen::B), + 0..=0x7 => Ok(IncompleteTruncatedOpen::Unknown(Private(value))), + _ => Err(value), + } + } +} +impl From<&IncompleteTruncatedOpen> for u8 { + fn from(value: &IncompleteTruncatedOpen) -> Self { + match value { + IncompleteTruncatedOpen::A => 0x0, + IncompleteTruncatedOpen::B => 0x1, + IncompleteTruncatedOpen::Unknown(Private(value)) => *value, + } + } +} +impl From<IncompleteTruncatedOpen> for u8 { + fn from(value: IncompleteTruncatedOpen) -> Self { + (&value).into() + } +} +impl From<IncompleteTruncatedOpen> for i8 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i16 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i32 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i64 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u16 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u32 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u64 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum IncompleteTruncatedClosedWithRange { + A, + X, + Y, + B(Private<u8>), +} +impl TryFrom<u8> for IncompleteTruncatedClosedWithRange { + type Error = u8; + fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { + match value { + 0x0 => Ok(IncompleteTruncatedClosedWithRange::A), + 0x1 => Ok(IncompleteTruncatedClosedWithRange::X), + 0x2 => Ok(IncompleteTruncatedClosedWithRange::Y), + 0x1..=0x6 => Ok(IncompleteTruncatedClosedWithRange::B(Private(value))), + _ => Err(value), + } + } +} +impl From<&IncompleteTruncatedClosedWithRange> for u8 { + fn from(value: &IncompleteTruncatedClosedWithRange) -> Self { + match value { + IncompleteTruncatedClosedWithRange::A => 0x0, + IncompleteTruncatedClosedWithRange::X => 0x1, + IncompleteTruncatedClosedWithRange::Y => 0x2, + IncompleteTruncatedClosedWithRange::B(Private(value)) => *value, + } + } +} +impl From<IncompleteTruncatedClosedWithRange> for u8 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + (&value).into() + } +} +impl From<IncompleteTruncatedClosedWithRange> for i8 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i16 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i32 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i64 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u16 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u32 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u64 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum IncompleteTruncatedOpenWithRange { A, X, Y, B(Private<u8>), + Unknown(Private<u8>), } -impl TryFrom<u8> for IncompleteTruncatedWithRange { +impl TryFrom<u8> for IncompleteTruncatedOpenWithRange { type Error = u8; fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { match value { - 0x0 => Ok(IncompleteTruncatedWithRange::A), - 0x1 => Ok(IncompleteTruncatedWithRange::X), - 0x2 => Ok(IncompleteTruncatedWithRange::Y), - 0x1..=0x6 => Ok(IncompleteTruncatedWithRange::B(Private(value))), + 0x0 => Ok(IncompleteTruncatedOpenWithRange::A), + 0x1 => Ok(IncompleteTruncatedOpenWithRange::X), + 0x2 => Ok(IncompleteTruncatedOpenWithRange::Y), + 0x1..=0x6 => Ok(IncompleteTruncatedOpenWithRange::B(Private(value))), + 0..=0x7 => Ok(IncompleteTruncatedOpenWithRange::Unknown(Private(value))), _ => Err(value), } } } -impl From<&IncompleteTruncatedWithRange> for u8 { - fn from(value: &IncompleteTruncatedWithRange) -> Self { +impl From<&IncompleteTruncatedOpenWithRange> for u8 { + fn from(value: &IncompleteTruncatedOpenWithRange) -> Self { match value { - IncompleteTruncatedWithRange::A => 0x0, - IncompleteTruncatedWithRange::X => 0x1, - IncompleteTruncatedWithRange::Y => 0x2, - IncompleteTruncatedWithRange::B(Private(value)) => *value, + IncompleteTruncatedOpenWithRange::A => 0x0, + IncompleteTruncatedOpenWithRange::X => 0x1, + IncompleteTruncatedOpenWithRange::Y => 0x2, + IncompleteTruncatedOpenWithRange::B(Private(value)) => *value, + IncompleteTruncatedOpenWithRange::Unknown(Private(value)) => *value, } } } -impl From<IncompleteTruncatedWithRange> for u8 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u8 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { (&value).into() } } -impl From<IncompleteTruncatedWithRange> for i8 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i8 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i16 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i16 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i32 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i32 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i64 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i64 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u16 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u16 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u32 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u32 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u64 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u64 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } diff --git a/tests/generated/enum_declaration_little_endian.rs b/tests/generated/enum_declaration_little_endian.rs index badb3f0..ffc4725 100644 --- a/tests/generated/enum_declaration_little_endian.rs +++ b/tests/generated/enum_declaration_little_endian.rs @@ -47,136 +47,278 @@ pub trait Packet { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] -pub enum IncompleteTruncated { +pub enum IncompleteTruncatedClosed { A = 0x0, B = 0x1, } -impl TryFrom<u8> for IncompleteTruncated { +impl TryFrom<u8> for IncompleteTruncatedClosed { type Error = u8; fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { match value { - 0x0 => Ok(IncompleteTruncated::A), - 0x1 => Ok(IncompleteTruncated::B), + 0x0 => Ok(IncompleteTruncatedClosed::A), + 0x1 => Ok(IncompleteTruncatedClosed::B), _ => Err(value), } } } -impl From<&IncompleteTruncated> for u8 { - fn from(value: &IncompleteTruncated) -> Self { +impl From<&IncompleteTruncatedClosed> for u8 { + fn from(value: &IncompleteTruncatedClosed) -> Self { match value { - IncompleteTruncated::A => 0x0, - IncompleteTruncated::B => 0x1, + IncompleteTruncatedClosed::A => 0x0, + IncompleteTruncatedClosed::B => 0x1, } } } -impl From<IncompleteTruncated> for u8 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u8 { + fn from(value: IncompleteTruncatedClosed) -> Self { (&value).into() } } -impl From<IncompleteTruncated> for i8 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i8 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i16 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i16 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i32 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i32 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for i64 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for i64 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u16 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u16 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u32 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u32 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncated> for u64 { - fn from(value: IncompleteTruncated) -> Self { +impl From<IncompleteTruncatedClosed> for u64 { + fn from(value: IncompleteTruncatedClosed) -> Self { u8::from(value) as Self } } #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] -pub enum IncompleteTruncatedWithRange { +pub enum IncompleteTruncatedOpen { + A, + B, + Unknown(Private<u8>), +} +impl TryFrom<u8> for IncompleteTruncatedOpen { + type Error = u8; + fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { + match value { + 0x0 => Ok(IncompleteTruncatedOpen::A), + 0x1 => Ok(IncompleteTruncatedOpen::B), + 0..=0x7 => Ok(IncompleteTruncatedOpen::Unknown(Private(value))), + _ => Err(value), + } + } +} +impl From<&IncompleteTruncatedOpen> for u8 { + fn from(value: &IncompleteTruncatedOpen) -> Self { + match value { + IncompleteTruncatedOpen::A => 0x0, + IncompleteTruncatedOpen::B => 0x1, + IncompleteTruncatedOpen::Unknown(Private(value)) => *value, + } + } +} +impl From<IncompleteTruncatedOpen> for u8 { + fn from(value: IncompleteTruncatedOpen) -> Self { + (&value).into() + } +} +impl From<IncompleteTruncatedOpen> for i8 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i16 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i32 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for i64 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u16 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u32 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedOpen> for u64 { + fn from(value: IncompleteTruncatedOpen) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum IncompleteTruncatedClosedWithRange { + A, + X, + Y, + B(Private<u8>), +} +impl TryFrom<u8> for IncompleteTruncatedClosedWithRange { + type Error = u8; + fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { + match value { + 0x0 => Ok(IncompleteTruncatedClosedWithRange::A), + 0x1 => Ok(IncompleteTruncatedClosedWithRange::X), + 0x2 => Ok(IncompleteTruncatedClosedWithRange::Y), + 0x1..=0x6 => Ok(IncompleteTruncatedClosedWithRange::B(Private(value))), + _ => Err(value), + } + } +} +impl From<&IncompleteTruncatedClosedWithRange> for u8 { + fn from(value: &IncompleteTruncatedClosedWithRange) -> Self { + match value { + IncompleteTruncatedClosedWithRange::A => 0x0, + IncompleteTruncatedClosedWithRange::X => 0x1, + IncompleteTruncatedClosedWithRange::Y => 0x2, + IncompleteTruncatedClosedWithRange::B(Private(value)) => *value, + } + } +} +impl From<IncompleteTruncatedClosedWithRange> for u8 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + (&value).into() + } +} +impl From<IncompleteTruncatedClosedWithRange> for i8 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i16 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i32 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for i64 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u16 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u32 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +impl From<IncompleteTruncatedClosedWithRange> for u64 { + fn from(value: IncompleteTruncatedClosedWithRange) -> Self { + u8::from(value) as Self + } +} +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))] +pub enum IncompleteTruncatedOpenWithRange { A, X, Y, B(Private<u8>), + Unknown(Private<u8>), } -impl TryFrom<u8> for IncompleteTruncatedWithRange { +impl TryFrom<u8> for IncompleteTruncatedOpenWithRange { type Error = u8; fn try_from(value: u8) -> std::result::Result<Self, Self::Error> { match value { - 0x0 => Ok(IncompleteTruncatedWithRange::A), - 0x1 => Ok(IncompleteTruncatedWithRange::X), - 0x2 => Ok(IncompleteTruncatedWithRange::Y), - 0x1..=0x6 => Ok(IncompleteTruncatedWithRange::B(Private(value))), + 0x0 => Ok(IncompleteTruncatedOpenWithRange::A), + 0x1 => Ok(IncompleteTruncatedOpenWithRange::X), + 0x2 => Ok(IncompleteTruncatedOpenWithRange::Y), + 0x1..=0x6 => Ok(IncompleteTruncatedOpenWithRange::B(Private(value))), + 0..=0x7 => Ok(IncompleteTruncatedOpenWithRange::Unknown(Private(value))), _ => Err(value), } } } -impl From<&IncompleteTruncatedWithRange> for u8 { - fn from(value: &IncompleteTruncatedWithRange) -> Self { +impl From<&IncompleteTruncatedOpenWithRange> for u8 { + fn from(value: &IncompleteTruncatedOpenWithRange) -> Self { match value { - IncompleteTruncatedWithRange::A => 0x0, - IncompleteTruncatedWithRange::X => 0x1, - IncompleteTruncatedWithRange::Y => 0x2, - IncompleteTruncatedWithRange::B(Private(value)) => *value, + IncompleteTruncatedOpenWithRange::A => 0x0, + IncompleteTruncatedOpenWithRange::X => 0x1, + IncompleteTruncatedOpenWithRange::Y => 0x2, + IncompleteTruncatedOpenWithRange::B(Private(value)) => *value, + IncompleteTruncatedOpenWithRange::Unknown(Private(value)) => *value, } } } -impl From<IncompleteTruncatedWithRange> for u8 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u8 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { (&value).into() } } -impl From<IncompleteTruncatedWithRange> for i8 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i8 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i16 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i16 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i32 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i32 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for i64 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for i64 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u16 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u16 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u32 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u32 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } -impl From<IncompleteTruncatedWithRange> for u64 { - fn from(value: IncompleteTruncatedWithRange) -> Self { +impl From<IncompleteTruncatedOpenWithRange> for u64 { + fn from(value: IncompleteTruncatedOpenWithRange) -> Self { u8::from(value) as Self } } diff --git a/tests/generated/packet_decl_24bit_enum_array_big_endian.rs b/tests/generated/packet_decl_24bit_enum_array_big_endian.rs index 47b45dc..7717d7a 100644 --- a/tests/generated/packet_decl_24bit_enum_array_big_endian.rs +++ b/tests/generated/packet_decl_24bit_enum_array_big_endian.rs @@ -125,10 +125,10 @@ impl BarData { let x = (0..5) .map(|_| { Foo::try_from(bytes.get_mut().get_uint(3) as u32) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_24bit_enum_array_little_endian.rs b/tests/generated/packet_decl_24bit_enum_array_little_endian.rs index a413ce1..2fb9a9c 100644 --- a/tests/generated/packet_decl_24bit_enum_array_little_endian.rs +++ b/tests/generated/packet_decl_24bit_enum_array_little_endian.rs @@ -125,10 +125,10 @@ impl BarData { let x = (0..5) .map(|_| { Foo::try_from(bytes.get_mut().get_uint_le(3) as u32) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_24bit_enum_big_endian.rs b/tests/generated/packet_decl_24bit_enum_big_endian.rs index c63b562..272e52d 100644 --- a/tests/generated/packet_decl_24bit_enum_big_endian.rs +++ b/tests/generated/packet_decl_24bit_enum_big_endian.rs @@ -123,10 +123,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_uint(3) as u32) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_uint(3) as u32 as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_24bit_enum_little_endian.rs b/tests/generated/packet_decl_24bit_enum_little_endian.rs index 2e58a9e..278336c 100644 --- a/tests/generated/packet_decl_24bit_enum_little_endian.rs +++ b/tests/generated/packet_decl_24bit_enum_little_endian.rs @@ -123,10 +123,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_uint_le(3) as u32) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_uint_le(3) as u32 as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_64bit_enum_array_big_endian.rs b/tests/generated/packet_decl_64bit_enum_array_big_endian.rs index dd28666..caeba78 100644 --- a/tests/generated/packet_decl_64bit_enum_array_big_endian.rs +++ b/tests/generated/packet_decl_64bit_enum_array_big_endian.rs @@ -110,10 +110,10 @@ impl BarData { let x = (0..7) .map(|_| { Foo::try_from(bytes.get_mut().get_u64()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_64bit_enum_array_little_endian.rs b/tests/generated/packet_decl_64bit_enum_array_little_endian.rs index 371c1eb..2a698b8 100644 --- a/tests/generated/packet_decl_64bit_enum_array_little_endian.rs +++ b/tests/generated/packet_decl_64bit_enum_array_little_endian.rs @@ -110,10 +110,10 @@ impl BarData { let x = (0..7) .map(|_| { Foo::try_from(bytes.get_mut().get_u64_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_64bit_enum_big_endian.rs b/tests/generated/packet_decl_64bit_enum_big_endian.rs index bd46742..f29c654 100644 --- a/tests/generated/packet_decl_64bit_enum_big_endian.rs +++ b/tests/generated/packet_decl_64bit_enum_big_endian.rs @@ -108,10 +108,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_u64()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_u64() as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_64bit_enum_little_endian.rs b/tests/generated/packet_decl_64bit_enum_little_endian.rs index 75a43b6..0bc5a12 100644 --- a/tests/generated/packet_decl_64bit_enum_little_endian.rs +++ b/tests/generated/packet_decl_64bit_enum_little_endian.rs @@ -108,10 +108,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_u64_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_u64_le() as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_8bit_enum_array_big_endian.rs b/tests/generated/packet_decl_8bit_enum_array_big_endian.rs index 4c16ddf..f36f1ba 100644 --- a/tests/generated/packet_decl_8bit_enum_array_big_endian.rs +++ b/tests/generated/packet_decl_8bit_enum_array_big_endian.rs @@ -140,10 +140,10 @@ impl BarData { let x = (0..3) .map(|_| { Foo::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_8bit_enum_array_little_endian.rs b/tests/generated/packet_decl_8bit_enum_array_little_endian.rs index 4c16ddf..f36f1ba 100644 --- a/tests/generated/packet_decl_8bit_enum_array_little_endian.rs +++ b/tests/generated/packet_decl_8bit_enum_array_little_endian.rs @@ -140,10 +140,10 @@ impl BarData { let x = (0..3) .map(|_| { Foo::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: String::new(), - value: 0, + value: unknown_val as u64, type_: "Foo".to_string(), }) }) diff --git a/tests/generated/packet_decl_8bit_enum_big_endian.rs b/tests/generated/packet_decl_8bit_enum_big_endian.rs index 8ceb132..bccdaeb 100644 --- a/tests/generated/packet_decl_8bit_enum_big_endian.rs +++ b/tests/generated/packet_decl_8bit_enum_big_endian.rs @@ -138,10 +138,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_8bit_enum_little_endian.rs b/tests/generated/packet_decl_8bit_enum_little_endian.rs index 8ceb132..bccdaeb 100644 --- a/tests/generated/packet_decl_8bit_enum_little_endian.rs +++ b/tests/generated/packet_decl_8bit_enum_little_endian.rs @@ -138,10 +138,10 @@ impl BarData { }); } let x = Foo::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Bar".to_string(), field: "x".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Foo".to_string(), })?; Ok(Self { x }) diff --git a/tests/generated/packet_decl_child_packets_big_endian.rs b/tests/generated/packet_decl_child_packets_big_endian.rs index 7a000c7..8b3e05d 100644 --- a/tests/generated/packet_decl_child_packets_big_endian.rs +++ b/tests/generated/packet_decl_child_packets_big_endian.rs @@ -166,10 +166,10 @@ impl FooData { }); } let b = Enum16::try_from(bytes.get_mut().get_u16()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "b".to_string(), - value: bytes.get_mut().get_u16() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 1 { diff --git a/tests/generated/packet_decl_child_packets_little_endian.rs b/tests/generated/packet_decl_child_packets_little_endian.rs index 7fe9783..8a464b2 100644 --- a/tests/generated/packet_decl_child_packets_little_endian.rs +++ b/tests/generated/packet_decl_child_packets_little_endian.rs @@ -166,10 +166,10 @@ impl FooData { }); } let b = Enum16::try_from(bytes.get_mut().get_u16_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "b".to_string(), - value: bytes.get_mut().get_u16_le() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 1 { diff --git a/tests/generated/packet_decl_fixed_enum_field_big_endian.rs b/tests/generated/packet_decl_fixed_enum_field_big_endian.rs index 2ec6b58..5c780ad 100644 --- a/tests/generated/packet_decl_fixed_enum_field_big_endian.rs +++ b/tests/generated/packet_decl_fixed_enum_field_big_endian.rs @@ -143,10 +143,11 @@ impl FooData { }); } let chunk = bytes.get_mut().get_u64(); - if (chunk & 0x7f) as u8 != u8::from(Enum7::A) { + let fixed_value = (chunk & 0x7f) as u8; + if fixed_value != u8::from(Enum7::A) { return Err(Error::InvalidFixedValue { expected: u8::from(Enum7::A) as u64, - actual: (chunk & 0x7f) as u8 as u64, + actual: fixed_value as u64, }); } let b = ((chunk >> 7) & 0x1ff_ffff_ffff_ffff_u64); diff --git a/tests/generated/packet_decl_fixed_enum_field_little_endian.rs b/tests/generated/packet_decl_fixed_enum_field_little_endian.rs index 13aecd9..45486c5 100644 --- a/tests/generated/packet_decl_fixed_enum_field_little_endian.rs +++ b/tests/generated/packet_decl_fixed_enum_field_little_endian.rs @@ -143,10 +143,11 @@ impl FooData { }); } let chunk = bytes.get_mut().get_u64_le(); - if (chunk & 0x7f) as u8 != u8::from(Enum7::A) { + let fixed_value = (chunk & 0x7f) as u8; + if fixed_value != u8::from(Enum7::A) { return Err(Error::InvalidFixedValue { expected: u8::from(Enum7::A) as u64, - actual: (chunk & 0x7f) as u8 as u64, + actual: fixed_value as u64, }); } let b = ((chunk >> 7) & 0x1ff_ffff_ffff_ffff_u64); diff --git a/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs b/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs index e047669..349b183 100644 --- a/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs +++ b/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs @@ -77,10 +77,11 @@ impl FooData { }); } let chunk = bytes.get_mut().get_u64(); + let fixed_value = (chunk & 0x7f) as u8; if (chunk & 0x7f) as u8 != 7 { return Err(Error::InvalidFixedValue { expected: 7, - actual: (chunk & 0x7f) as u8 as u64, + actual: fixed_value as u64, }); } let b = ((chunk >> 7) & 0x1ff_ffff_ffff_ffff_u64); diff --git a/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs b/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs index 39922ca..96b9064 100644 --- a/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs +++ b/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs @@ -77,10 +77,11 @@ impl FooData { }); } let chunk = bytes.get_mut().get_u64_le(); + let fixed_value = (chunk & 0x7f) as u8; if (chunk & 0x7f) as u8 != 7 { return Err(Error::InvalidFixedValue { expected: 7, - actual: (chunk & 0x7f) as u8 as u64, + actual: fixed_value as u64, }); } let b = ((chunk >> 7) & 0x1ff_ffff_ffff_ffff_u64); diff --git a/tests/generated/packet_decl_grand_children_big_endian.rs b/tests/generated/packet_decl_grand_children_big_endian.rs index 896a46f..f1a2cac 100644 --- a/tests/generated/packet_decl_grand_children_big_endian.rs +++ b/tests/generated/packet_decl_grand_children_big_endian.rs @@ -157,10 +157,10 @@ impl ParentData { }); } let foo = Enum16::try_from(bytes.get_mut().get_u16()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "foo".to_string(), - value: bytes.get_mut().get_u16() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 2 { @@ -171,10 +171,10 @@ impl ParentData { }); } let bar = Enum16::try_from(bytes.get_mut().get_u16()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "bar".to_string(), - value: bytes.get_mut().get_u16() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 2 { @@ -185,10 +185,10 @@ impl ParentData { }); } let baz = Enum16::try_from(bytes.get_mut().get_u16()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "baz".to_string(), - value: bytes.get_mut().get_u16() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 1 { @@ -389,10 +389,10 @@ impl ChildData { }); } let quux = Enum16::try_from(bytes.get_mut().get_u16()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Child".to_string(), field: "quux".to_string(), - value: bytes.get_mut().get_u16() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; let payload = bytes.get(); diff --git a/tests/generated/packet_decl_grand_children_little_endian.rs b/tests/generated/packet_decl_grand_children_little_endian.rs index 4a672a6..66fa76a 100644 --- a/tests/generated/packet_decl_grand_children_little_endian.rs +++ b/tests/generated/packet_decl_grand_children_little_endian.rs @@ -157,10 +157,10 @@ impl ParentData { }); } let foo = Enum16::try_from(bytes.get_mut().get_u16_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "foo".to_string(), - value: bytes.get_mut().get_u16_le() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 2 { @@ -171,10 +171,10 @@ impl ParentData { }); } let bar = Enum16::try_from(bytes.get_mut().get_u16_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "bar".to_string(), - value: bytes.get_mut().get_u16_le() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 2 { @@ -185,10 +185,10 @@ impl ParentData { }); } let baz = Enum16::try_from(bytes.get_mut().get_u16_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "baz".to_string(), - value: bytes.get_mut().get_u16_le() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; if bytes.get().remaining() < 1 { @@ -389,10 +389,10 @@ impl ChildData { }); } let quux = Enum16::try_from(bytes.get_mut().get_u16_le()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Child".to_string(), field: "quux".to_string(), - value: bytes.get_mut().get_u16_le() as u64, + value: unknown_val as u64, type_: "Enum16".to_string(), })?; let payload = bytes.get(); diff --git a/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs b/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs index 122b202..7dcf2e6 100644 --- a/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs +++ b/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs @@ -206,18 +206,18 @@ impl FooData { } let chunk = bytes.get_mut().get_uint(3) as u32; let x = Enum7::try_from((chunk & 0x7f) as u8) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "x".to_string(), - value: (chunk & 0x7f) as u8 as u64, + value: unknown_val as u64, type_: "Enum7".to_string(), })?; let y = ((chunk >> 7) & 0x1f) as u8; let z = Enum9::try_from(((chunk >> 12) & 0x1ff) as u16) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "z".to_string(), - value: ((chunk >> 12) & 0x1ff) as u16 as u64, + value: unknown_val as u64, type_: "Enum9".to_string(), })?; let w = ((chunk >> 21) & 0x7) as u8; diff --git a/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs b/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs index 0cc3d92..667dab3 100644 --- a/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs +++ b/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs @@ -206,18 +206,18 @@ impl FooData { } let chunk = bytes.get_mut().get_uint_le(3) as u32; let x = Enum7::try_from((chunk & 0x7f) as u8) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "x".to_string(), - value: (chunk & 0x7f) as u8 as u64, + value: unknown_val as u64, type_: "Enum7".to_string(), })?; let y = ((chunk >> 7) & 0x1f) as u8; let z = Enum9::try_from(((chunk >> 12) & 0x1ff) as u16) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Foo".to_string(), field: "z".to_string(), - value: ((chunk >> 12) & 0x1ff) as u16 as u64, + value: unknown_val as u64, type_: "Enum9".to_string(), })?; let w = ((chunk >> 21) & 0x7) as u8; diff --git a/tests/generated/packet_decl_parent_with_alias_child_big_endian.rs b/tests/generated/packet_decl_parent_with_alias_child_big_endian.rs index 8c9232d..785c0ee 100644 --- a/tests/generated/packet_decl_parent_with_alias_child_big_endian.rs +++ b/tests/generated/packet_decl_parent_with_alias_child_big_endian.rs @@ -169,10 +169,10 @@ impl ParentData { }); } let v = Enum8::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "v".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Enum8".to_string(), })?; let payload = bytes.get(); diff --git a/tests/generated/packet_decl_parent_with_alias_child_little_endian.rs b/tests/generated/packet_decl_parent_with_alias_child_little_endian.rs index 8c9232d..785c0ee 100644 --- a/tests/generated/packet_decl_parent_with_alias_child_little_endian.rs +++ b/tests/generated/packet_decl_parent_with_alias_child_little_endian.rs @@ -169,10 +169,10 @@ impl ParentData { }); } let v = Enum8::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "v".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Enum8".to_string(), })?; let payload = bytes.get(); diff --git a/tests/generated/packet_decl_parent_with_no_payload_big_endian.rs b/tests/generated/packet_decl_parent_with_no_payload_big_endian.rs index 4f58e26..82c45c5 100644 --- a/tests/generated/packet_decl_parent_with_no_payload_big_endian.rs +++ b/tests/generated/packet_decl_parent_with_no_payload_big_endian.rs @@ -160,10 +160,10 @@ impl ParentData { }); } let v = Enum8::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "v".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Enum8".to_string(), })?; let payload: &[u8] = &[]; diff --git a/tests/generated/packet_decl_parent_with_no_payload_little_endian.rs b/tests/generated/packet_decl_parent_with_no_payload_little_endian.rs index 4f58e26..82c45c5 100644 --- a/tests/generated/packet_decl_parent_with_no_payload_little_endian.rs +++ b/tests/generated/packet_decl_parent_with_no_payload_little_endian.rs @@ -160,10 +160,10 @@ impl ParentData { }); } let v = Enum8::try_from(bytes.get_mut().get_u8()) - .map_err(|_| Error::InvalidEnumValueError { + .map_err(|unknown_val| Error::InvalidEnumValueError { obj: "Parent".to_string(), field: "v".to_string(), - value: bytes.get_mut().get_u8() as u64, + value: unknown_val as u64, type_: "Enum8".to_string(), })?; let payload: &[u8] = &[]; diff --git a/tests/generated/reserved_identifier_big_endian.rs b/tests/generated/reserved_identifier_big_endian.rs new file mode 100644 index 0000000..1f13532 --- /dev/null +++ b/tests/generated/reserved_identifier_big_endian.rs @@ -0,0 +1,145 @@ +#![rustfmt::skip] +/// @generated rust packets from test. +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use std::convert::{TryFrom, TryInto}; +use std::cell::Cell; +use std::fmt; +use thiserror::Error; +type Result<T> = std::result::Result<T, Error>; +/// Private prevents users from creating arbitrary scalar values +/// in situations where the value needs to be validated. +/// Users can freely deref the value, but only the backend +/// may create it. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Private<T>(T); +impl<T> std::ops::Deref for Private<T> { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +#[derive(Debug, Error)] +pub enum Error { + #[error("Packet parsing failed")] + InvalidPacketError, + #[error("{field} was {value:x}, which is not known")] + ConstraintOutOfBounds { field: String, value: u64 }, + #[error("Got {actual:x}, expected {expected:x}")] + InvalidFixedValue { expected: u64, actual: u64 }, + #[error("when parsing {obj} needed length of {wanted} but got {got}")] + InvalidLengthError { obj: String, wanted: usize, got: usize }, + #[error( + "array size ({array} bytes) is not a multiple of the element size ({element} bytes)" + )] + InvalidArraySize { array: usize, element: usize }, + #[error("Due to size restrictions a struct could not be parsed.")] + ImpossibleStructError, + #[error("when parsing field {obj}.{field}, {value} is not a valid {type_} value")] + InvalidEnumValueError { obj: String, field: String, value: u64, type_: String }, + #[error("expected child {expected}, got {actual}")] + InvalidChildError { expected: &'static str, actual: String }, +} +pub trait Packet { + fn to_bytes(self) -> Bytes; + fn to_vec(self) -> Vec<u8>; +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TestData { + r#type: u8, +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Test { + #[cfg_attr(feature = "serde", serde(flatten))] + test: TestData, +} +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TestBuilder { + pub r#type: u8, +} +impl TestData { + fn conforms(bytes: &[u8]) -> bool { + bytes.len() >= 1 + } + fn parse(bytes: &[u8]) -> Result<Self> { + let mut cell = Cell::new(bytes); + let packet = Self::parse_inner(&mut cell)?; + Ok(packet) + } + fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self> { + if bytes.get().remaining() < 1 { + return Err(Error::InvalidLengthError { + obj: "Test".to_string(), + wanted: 1, + got: bytes.get().remaining(), + }); + } + let r#type = bytes.get_mut().get_u8(); + Ok(Self { r#type }) + } + fn write_to(&self, buffer: &mut BytesMut) { + buffer.put_u8(self.r#type); + } + fn get_total_size(&self) -> usize { + self.get_size() + } + fn get_size(&self) -> usize { + 1 + } +} +impl Packet for Test { + fn to_bytes(self) -> Bytes { + let mut buffer = BytesMut::with_capacity(self.test.get_size()); + self.test.write_to(&mut buffer); + buffer.freeze() + } + fn to_vec(self) -> Vec<u8> { + self.to_bytes().to_vec() + } +} +impl From<Test> for Bytes { + fn from(packet: Test) -> Self { + packet.to_bytes() + } +} +impl From<Test> for Vec<u8> { + fn from(packet: Test) -> Self { + packet.to_vec() + } +} +impl Test { + pub fn parse(bytes: &[u8]) -> Result<Self> { + let mut cell = Cell::new(bytes); + let packet = Self::parse_inner(&mut cell)?; + Ok(packet) + } + fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self> { + let data = TestData::parse_inner(&mut bytes)?; + Self::new(data) + } + fn new(test: TestData) -> Result<Self> { + Ok(Self { test }) + } + pub fn get_type(&self) -> u8 { + self.test.r#type + } + fn write_to(&self, buffer: &mut BytesMut) { + self.test.write_to(buffer) + } + pub fn get_size(&self) -> usize { + self.test.get_size() + } +} +impl TestBuilder { + pub fn build(self) -> Test { + let test = TestData { r#type: self.r#type }; + Test::new(test).unwrap() + } +} +impl From<TestBuilder> for Test { + fn from(builder: TestBuilder) -> Test { + builder.build().into() + } +} diff --git a/tests/generated/reserved_identifier_little_endian.rs b/tests/generated/reserved_identifier_little_endian.rs new file mode 100644 index 0000000..1f13532 --- /dev/null +++ b/tests/generated/reserved_identifier_little_endian.rs @@ -0,0 +1,145 @@ +#![rustfmt::skip] +/// @generated rust packets from test. +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use std::convert::{TryFrom, TryInto}; +use std::cell::Cell; +use std::fmt; +use thiserror::Error; +type Result<T> = std::result::Result<T, Error>; +/// Private prevents users from creating arbitrary scalar values +/// in situations where the value needs to be validated. +/// Users can freely deref the value, but only the backend +/// may create it. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Private<T>(T); +impl<T> std::ops::Deref for Private<T> { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +#[derive(Debug, Error)] +pub enum Error { + #[error("Packet parsing failed")] + InvalidPacketError, + #[error("{field} was {value:x}, which is not known")] + ConstraintOutOfBounds { field: String, value: u64 }, + #[error("Got {actual:x}, expected {expected:x}")] + InvalidFixedValue { expected: u64, actual: u64 }, + #[error("when parsing {obj} needed length of {wanted} but got {got}")] + InvalidLengthError { obj: String, wanted: usize, got: usize }, + #[error( + "array size ({array} bytes) is not a multiple of the element size ({element} bytes)" + )] + InvalidArraySize { array: usize, element: usize }, + #[error("Due to size restrictions a struct could not be parsed.")] + ImpossibleStructError, + #[error("when parsing field {obj}.{field}, {value} is not a valid {type_} value")] + InvalidEnumValueError { obj: String, field: String, value: u64, type_: String }, + #[error("expected child {expected}, got {actual}")] + InvalidChildError { expected: &'static str, actual: String }, +} +pub trait Packet { + fn to_bytes(self) -> Bytes; + fn to_vec(self) -> Vec<u8>; +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TestData { + r#type: u8, +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Test { + #[cfg_attr(feature = "serde", serde(flatten))] + test: TestData, +} +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TestBuilder { + pub r#type: u8, +} +impl TestData { + fn conforms(bytes: &[u8]) -> bool { + bytes.len() >= 1 + } + fn parse(bytes: &[u8]) -> Result<Self> { + let mut cell = Cell::new(bytes); + let packet = Self::parse_inner(&mut cell)?; + Ok(packet) + } + fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self> { + if bytes.get().remaining() < 1 { + return Err(Error::InvalidLengthError { + obj: "Test".to_string(), + wanted: 1, + got: bytes.get().remaining(), + }); + } + let r#type = bytes.get_mut().get_u8(); + Ok(Self { r#type }) + } + fn write_to(&self, buffer: &mut BytesMut) { + buffer.put_u8(self.r#type); + } + fn get_total_size(&self) -> usize { + self.get_size() + } + fn get_size(&self) -> usize { + 1 + } +} +impl Packet for Test { + fn to_bytes(self) -> Bytes { + let mut buffer = BytesMut::with_capacity(self.test.get_size()); + self.test.write_to(&mut buffer); + buffer.freeze() + } + fn to_vec(self) -> Vec<u8> { + self.to_bytes().to_vec() + } +} +impl From<Test> for Bytes { + fn from(packet: Test) -> Self { + packet.to_bytes() + } +} +impl From<Test> for Vec<u8> { + fn from(packet: Test) -> Self { + packet.to_vec() + } +} +impl Test { + pub fn parse(bytes: &[u8]) -> Result<Self> { + let mut cell = Cell::new(bytes); + let packet = Self::parse_inner(&mut cell)?; + Ok(packet) + } + fn parse_inner(mut bytes: &mut Cell<&[u8]>) -> Result<Self> { + let data = TestData::parse_inner(&mut bytes)?; + Self::new(data) + } + fn new(test: TestData) -> Result<Self> { + Ok(Self { test }) + } + pub fn get_type(&self) -> u8 { + self.test.r#type + } + fn write_to(&self, buffer: &mut BytesMut) { + self.test.write_to(buffer) + } + pub fn get_size(&self) -> usize { + self.test.get_size() + } +} +impl TestBuilder { + pub fn build(self) -> Test { + let test = TestData { r#type: self.r#type }; + Test::new(test).unwrap() + } +} +impl From<TestBuilder> for Test { + fn from(builder: TestBuilder) -> Test { + builder.build().into() + } +} |