Upgrade pdl-compiler to 0.2.2

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update rust/crates/pdl-compiler
For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md

Test: TreeHugger
Change-Id: I4151e72a15bcac21c38b734ac3ba71fde1b3d0b3
diff --git a/Android.bp b/Android.bp
index 067bed1..e810fca 100644
--- a/Android.bp
+++ b/Android.bp
@@ -7,7 +7,7 @@
     name: "generate_canonical_tests",
     crate_name: "generate_canonical_tests",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.6",
+    cargo_pkg_version: "0.2.2",
     srcs: ["src/bin/generate-canonical-tests.rs"],
     edition: "2021",
     features: [
@@ -35,7 +35,7 @@
     name: "libpdl_compiler",
     crate_name: "pdl_compiler",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.6",
+    cargo_pkg_version: "0.2.2",
     srcs: ["src/lib.rs"],
     edition: "2021",
     features: [
@@ -62,7 +62,7 @@
     name: "pdlc",
     crate_name: "pdlc",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.1.6",
+    cargo_pkg_version: "0.2.2",
     srcs: ["src/main.rs"],
     edition: "2021",
     features: [
@@ -266,13 +266,6 @@
 }
 
 genrule {
-    name: "pdl_be_rust_test_file",
-    defaults: ["pdl_be_test_file_defaults"],
-    srcs: ["tests/canonical/le_rust_test_file.pdl"],
-    out: ["be_rust_test_file.pdl"],
-}
-
-genrule {
     name: "pdl_be_test_file",
     defaults: ["pdl_be_test_file_defaults"],
     srcs: ["tests/canonical/le_test_file.pdl"],
@@ -282,15 +275,57 @@
 // Generate the Rust parser+serializer backends.
 genrule {
     name: "pdl_le_backend",
-    defaults: ["pdl_rust_generator_defaults"],
-    srcs: ["tests/canonical/le_rust_test_file.pdl"],
+    tools: [":pdlc"],
+    cmd: "$(location :pdlc)" +
+        " --output-format rust" +
+        " --exclude-declaration UnsizedCustomField" +
+        " --exclude-declaration Packet_Custom_Field_VariableSize" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize_" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize" +
+        " --exclude-declaration Checksum" +
+        " --exclude-declaration Packet_Checksum_Field_FromStart" +
+        " --exclude-declaration Packet_Checksum_Field_FromEnd" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart_" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd_" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd" +
+        " --exclude-declaration PartialParent5" +
+        " --exclude-declaration PartialParent12" +
+        " --exclude-declaration PartialChild5_A" +
+        " --exclude-declaration PartialChild5_B" +
+        " --exclude-declaration PartialChild12_A" +
+        " --exclude-declaration PartialChild12_B" +
+        " --exclude-declaration Packet_Payload_Field_SizeModifier" +
+        " --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier" +
+        " $(in) > $(out)",
+    srcs: ["tests/canonical/le_test_file.pdl"],
     out: ["le_backend.rs"],
 }
 
 genrule {
     name: "pdl_be_backend",
-    defaults: ["pdl_rust_generator_defaults"],
-    srcs: [":pdl_be_rust_test_file"],
+    tools: [":pdlc"],
+    cmd: "$(location :pdlc)" +
+        " --output-format rust" +
+        " --exclude-declaration UnsizedCustomField" +
+        " --exclude-declaration Packet_Custom_Field_VariableSize" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize_" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize" +
+        " --exclude-declaration Checksum" +
+        " --exclude-declaration Packet_Checksum_Field_FromStart" +
+        " --exclude-declaration Packet_Checksum_Field_FromEnd" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart_" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd_" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd" +
+        " --exclude-declaration Packet_Payload_Field_SizeModifier" +
+        " --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier" +
+        " $(in) > $(out)",
+    srcs: [":pdl_be_test_file"],
     out: ["be_backend.rs"],
 }
 
@@ -299,14 +334,9 @@
     features: ["serde"],
     rustlibs: [
         "libbytes",
-        "libnum_traits",
         "libserde",
         "libtempfile",
-        "libthiserror",
-    ],
-    proc_macros: [
-        "libnum_derive",
-        "libserde_derive",
+        "libpdl_runtime",
     ],
 }
 
@@ -351,6 +381,7 @@
     rustlibs: [
         "libnum_traits",
         "libpdl_le_backend",
+        "libpdl_runtime",
         "libserde_json",
     ],
     clippy_lints: "none",
@@ -364,6 +395,7 @@
     rustlibs: [
         "libnum_traits",
         "libpdl_be_backend",
+        "libpdl_runtime",
         "libserde_json",
     ],
     clippy_lints: "none",
@@ -446,28 +478,6 @@
     out: ["_packets.rs"],
 }
 
-// Generate the rust_noalloc test harness srcs for the supplied test vectors
-genrule {
-    name: "pdl_rust_noalloc_le_test_gen_harness",
-    cmd: "set -o pipefail;" +
-        " $(location :pdlc) $(in) --output-format rust_no_alloc_test" +
-        " > $(out)",
-    srcs: ["tests/canonical/le_rust_noalloc_test_file.pdl"],
-    out: ["test_rust_noalloc_parser.rs"],
-    tools: [":pdlc"],
-}
-
-// The test target for rust_noalloc
-rust_test_host {
-    name: "pdl_rust_noalloc_le_test",
-    srcs: [
-        ":pdl_rust_noalloc_le_test_gen_harness",
-
-        ":pdl_rust_noalloc_le_test_backend_srcs",
-    ],
-    test_suites: ["general-tests"],
-}
-
 // Generate the C++ parser+serializer backend for the
 // little endian test file located at tests/canonical/le_test_file.pdl.
 genrule {
diff --git a/Cargo.toml b/Cargo.toml
index 06d72e0..411fd67 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,16 +12,13 @@
 [package]
 edition = "2021"
 name = "pdl-compiler"
-version = "0.1.6"
+version = "0.2.2"
 authors = [
     "Henri Chataing <[email protected]>",
     "David de Jesus Duarte <[email protected]>",
     "Martin Geisler <[email protected]>",
 ]
-exclude = [
-    ".github/*",
-    "editors/*",
-]
+exclude = ["editors/*"]
 default-run = "pdlc"
 description = "Parser and serializer generator for protocol binary packets"
 readme = "README.md"
@@ -34,6 +31,7 @@
 categories = ["parsing"]
 license = "Apache-2.0"
 repository = "https://github.com/google/pdl/"
+resolver = "1"
 
 [[bin]]
 name = "pdlc"
@@ -85,7 +83,7 @@
 features = ["serde"]
 
 [dev-dependencies.googletest]
-version = "0.8.0"
+version = "0.10.0"
 
 [dev-dependencies.num-derive]
 version = "0.3.3"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f1e9ae0..a50b93f 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,18 +1,18 @@
 [package]
 name = "pdl-compiler"
-version = "0.1.6"
+version = "0.2.2"
 edition = "2021"
 description = "Parser and serializer generator for protocol binary packets"
 repository = "https://github.com/google/pdl/"
 license = "Apache-2.0"
-readme = "README.md"
+readme = "../README.md"
 keywords = ["pdl", "parser", "serializer", "grammar"]
 authors = [
     "Henri Chataing <[email protected]>",
     "David de Jesus Duarte <[email protected]>",
     "Martin Geisler <[email protected]>"
 ]
-exclude = [".github/*", "editors/*"]
+exclude = ["editors/*"]
 categories = ["parsing"]
 default-run = "pdlc"
 
@@ -20,8 +20,6 @@
 name = "pdlc"
 path = "src/main.rs"
 
-[workspace]
-
 [features]
 default = ["serde"]
 
@@ -49,4 +47,4 @@
 num-traits = "0.2.15"
 thiserror = "1.0.37"
 paste = "1.0.6"
-googletest = "0.8.0"
+googletest = "0.10.0"
diff --git a/README.md b/README.md
index 4fae445..1396b3d 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@
 - Scalar values
 - Enumerators
 - Arrays
+- Optional fields
 - Nested packets
 - Conditional packet derivation
 - Custom field definitions
diff --git a/cargo2android_toplevel.bp b/cargo2android_toplevel.bp
index c881dd0..123dff1 100644
--- a/cargo2android_toplevel.bp
+++ b/cargo2android_toplevel.bp
@@ -178,13 +178,6 @@
 }
 
 genrule {
-    name: "pdl_be_rust_test_file",
-    defaults: ["pdl_be_test_file_defaults"],
-    srcs: ["tests/canonical/le_rust_test_file.pdl"],
-    out: ["be_rust_test_file.pdl"],
-}
-
-genrule {
     name: "pdl_be_test_file",
     defaults: ["pdl_be_test_file_defaults"],
     srcs: ["tests/canonical/le_test_file.pdl"],
@@ -194,15 +187,57 @@
 // Generate the Rust parser+serializer backends.
 genrule {
     name: "pdl_le_backend",
-    defaults: ["pdl_rust_generator_defaults"],
-    srcs: ["tests/canonical/le_rust_test_file.pdl"],
+    tools: [":pdlc"],
+    cmd: "$(location :pdlc)" +
+        " --output-format rust" +
+        " --exclude-declaration UnsizedCustomField" +
+        " --exclude-declaration Packet_Custom_Field_VariableSize" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize_" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize" +
+        " --exclude-declaration Checksum" +
+        " --exclude-declaration Packet_Checksum_Field_FromStart" +
+        " --exclude-declaration Packet_Checksum_Field_FromEnd" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart_" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd_" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd" +
+        " --exclude-declaration PartialParent5" +
+        " --exclude-declaration PartialParent12" +
+        " --exclude-declaration PartialChild5_A" +
+        " --exclude-declaration PartialChild5_B" +
+        " --exclude-declaration PartialChild12_A" +
+        " --exclude-declaration PartialChild12_B" +
+        " --exclude-declaration Packet_Payload_Field_SizeModifier" +
+        " --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier" +
+        " $(in) > $(out)",
+    srcs: ["tests/canonical/le_test_file.pdl"],
     out: ["le_backend.rs"],
 }
 
 genrule {
     name: "pdl_be_backend",
-    defaults: ["pdl_rust_generator_defaults"],
-    srcs: [":pdl_be_rust_test_file"],
+    tools: [":pdlc"],
+    cmd: "$(location :pdlc)" +
+        " --output-format rust" +
+        " --exclude-declaration UnsizedCustomField" +
+        " --exclude-declaration Packet_Custom_Field_VariableSize" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize_" +
+        " --exclude-declaration Struct_Custom_Field_VariableSize" +
+        " --exclude-declaration Checksum" +
+        " --exclude-declaration Packet_Checksum_Field_FromStart" +
+        " --exclude-declaration Packet_Checksum_Field_FromEnd" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart_" +
+        " --exclude-declaration Struct_Checksum_Field_FromStart" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd_" +
+        " --exclude-declaration Struct_Checksum_Field_FromEnd" +
+        " --exclude-declaration Packet_Payload_Field_SizeModifier" +
+        " --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_" +
+        " --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier" +
+        " $(in) > $(out)",
+    srcs: [":pdl_be_test_file"],
     out: ["be_backend.rs"],
 }
 
@@ -211,14 +246,9 @@
     features: ["serde"],
     rustlibs: [
         "libbytes",
-        "libnum_traits",
         "libserde",
         "libtempfile",
-        "libthiserror",
-    ],
-    proc_macros: [
-        "libnum_derive",
-        "libserde_derive",
+        "libpdl_runtime",
     ],
 }
 
@@ -263,6 +293,7 @@
     rustlibs: [
         "libnum_traits",
         "libpdl_le_backend",
+        "libpdl_runtime",
         "libserde_json",
     ],
     clippy_lints: "none",
@@ -276,6 +307,7 @@
     rustlibs: [
         "libnum_traits",
         "libpdl_be_backend",
+        "libpdl_runtime",
         "libserde_json",
     ],
     clippy_lints: "none",
@@ -358,28 +390,6 @@
     out: ["_packets.rs"],
 }
 
-// Generate the rust_noalloc test harness srcs for the supplied test vectors
-genrule {
-    name: "pdl_rust_noalloc_le_test_gen_harness",
-    cmd: "set -o pipefail;" +
-        " $(location :pdlc) $(in) --output-format rust_no_alloc_test" +
-        " > $(out)",
-    srcs: ["tests/canonical/le_rust_noalloc_test_file.pdl"],
-    out: ["test_rust_noalloc_parser.rs"],
-    tools: [":pdlc"],
-}
-
-// The test target for rust_noalloc
-rust_test_host {
-    name: "pdl_rust_noalloc_le_test",
-    srcs: [
-        ":pdl_rust_noalloc_le_test_gen_harness",
-
-        ":pdl_rust_noalloc_le_test_backend_srcs",
-    ],
-    test_suites: ["general-tests"],
-}
-
 // Generate the C++ parser+serializer backend for the
 // little endian test file located at tests/canonical/le_test_file.pdl.
 genrule {
diff --git a/patches/0002-Ensure-compatibility-with-python-3.8.patch b/patches/0002-Ensure-compatibility-with-python-3.8.patch
new file mode 100644
index 0000000..60ece50
--- /dev/null
+++ b/patches/0002-Ensure-compatibility-with-python-3.8.patch
@@ -0,0 +1,43 @@
+From 3c642426ea83629c1eedbf5fc877fe51f01ed234 Mon Sep 17 00:00:00 2001
+From: Henri Chataing <[email protected]>
+Date: Thu, 2 Nov 2023 22:10:06 +0000
+Subject: [PATCH] Ensure compatibility with python 3.8
+
+Change-Id: I8db6ec0239fca889b39317c91f75bf0ddc4f3741
+---
+ scripts/pdl/ast.py | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/scripts/pdl/ast.py b/scripts/pdl/ast.py
+index 4aff1cc..5ade0fa 100644
+--- a/scripts/pdl/ast.py
++++ b/scripts/pdl/ast.py
+@@ -66,7 +66,7 @@ class Constraint(Node):
+ @dataclass
+ class Field(Node):
+     parent: Node = field(init=False)
+-    cond: Optional[Constraint] = field(kw_only=True, default=None)
++    cond: Optional[Constraint] = field(init=False, default=None)
+     # Backlink to the (optional) optional field referencing
+     # this field as condition.
+     cond_for: Optional['Field'] = field(init=False, default=None)
+@@ -277,8 +277,14 @@ def convert_(obj: object) -> object:
+         loc = SourceRange(loc['file'], SourceLocation(**loc['start']), SourceLocation(**loc['end']))
+         constructor = constructors_.get(kind)
+         members = {'loc': loc, 'kind': kind}
++        cond = None
+         for name, value in obj.items():
+-            if name != 'kind' and name != 'loc':
++            if name == 'cond':
++                cond = convert_(value)
++            elif name != 'kind' and name != 'loc':
+                 members[name] = convert_(value)
+-        return constructor(**members)
++        val = constructor(**members)
++        if cond:
++            val.cond = cond
++        return val
+     raise Exception('Unhandled json object type')
+-- 
+2.42.0.869.gea05f2083d-goog
+
diff --git a/scripts/generate_cxx_backend.py b/scripts/generate_cxx_backend.py
index b4f4efc..4937079 100755
--- a/scripts/generate_cxx_backend.py
+++ b/scripts/generate_cxx_backend.py
@@ -121,7 +121,9 @@
         for shift, width, field in self.chunk:
             v = (value if len(self.chunk) == 1 and shift == 0 else f"({value} >> {shift}) & {mask(width)}")
 
-            if isinstance(field, ast.ScalarField):
+            if field.cond_for:
+                self.unchecked_append_(f"uint8_t {field.id} = {v};")
+            elif isinstance(field, ast.ScalarField):
                 self.unchecked_append_(f"{field.id}_ = {v};")
             elif isinstance(field, ast.FixedField) and field.enum_id:
                 self.unchecked_append_(f"if ({field.enum_id}({v}) != {field.enum_id}::{field.tag_id}) {{")
@@ -159,6 +161,63 @@
                 return false;
             }}""".format(field_type=field.type.id, field_id=field.id)))
 
+    def parse_optional_field_(self, field: ast.Field):
+        """Parse the selected optional field.
+        Optional fields must start and end on a byte boundary."""
+
+        self.check_code_()
+
+        if isinstance(field, ast.ScalarField):
+            backing_type = get_cxx_scalar_type(field.width)
+            self.append_(dedent("""
+            if ({cond_id} == {cond_value}) {{
+                if (span.size() < {size}) {{
+                    return false;
+                }}
+                {field_id}_ = std::make_optional(
+                    span.read_{byteorder}<{backing_type}, {size}>());
+            }}
+            """.format(size=int(field.width / 8),
+                       backing_type=backing_type,
+                       field_id=field.id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value,
+                       byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
+            backing_type = get_cxx_scalar_type(field.type.width)
+            self.append_(dedent("""
+            if ({cond_id} == {cond_value}) {{
+                if (span.size() < {size}) {{
+                    return false;
+                }}
+                {field_id}_ = std::make_optional({type_id}(
+                    span.read_{byteorder}<{backing_type}, {size}>()));
+            }}
+            """.format(size=int(field.type.width / 8),
+                       backing_type=backing_type,
+                       type_id=field.type_id,
+                       field_id=field.id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value,
+                       byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField):
+            self.append_(dedent("""
+            if ({cond_id} == {cond_value}) {{
+                auto& output = {field_id}_.emplace();
+                if (!{type_id}::Parse(span, &output)) {{
+                    return false;
+                }}
+            }}
+            """.format(type_id=field.type_id,
+                       field_id=field.id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value)))
+
+        else:
+            raise Exception(f"unsupported field type {field.__class__.__name__}")
+
     def parse_array_field_lite_(self, field: ast.ArrayField):
         """Parse the selected array field.
         This function does not attempt to parse all elements but just to
@@ -459,7 +518,10 @@
         # Field has bit granularity.
         # Append the field to the current chunk,
         # check if a byte boundary was reached.
-        if core.is_bit_field(field):
+        if field.cond:
+            self.parse_optional_field_(field)
+
+        elif core.is_bit_field(field):
             self.parse_bit_field_(field)
 
         # Padding fields.
@@ -594,7 +656,11 @@
         width = core.get_field_size(field)
         shift = self.shift
 
-        if isinstance(field, ast.ScalarField):
+        if field.cond_for:
+            value_present = field.cond_for.cond.value
+            value_absent = 0 if field.cond_for.cond.value else 1
+            self.value.append((f"({field.cond_for.id}_.has_value() ? {value_present} : {value_absent})", shift))
+        elif isinstance(field, ast.ScalarField):
             self.value.append((f"{var} & {mask(field.width)}", shift))
         elif isinstance(field, ast.FixedField) and field.enum_id:
             self.value.append((f"{field.enum_id}::{field.tag_id}", shift))
@@ -668,6 +734,42 @@
 
         self.append_(f"{var}.Serialize(output);")
 
+    def serialize_optional_field_(self, field: ast.Field):
+        """Serialize optional scalar or typedef fields."""
+
+        if isinstance(field, ast.ScalarField):
+            backing_type = get_cxx_scalar_type(field.width)
+            self.append_(dedent(
+                """
+                if ({field_id}_.has_value()) {{
+                    pdl::packet::Builder::write_{byteorder}<{backing_type}, {size}>(output, {field_id}_.value());
+                }}""".format(field_id=field.id,
+                            size=int(field.width / 8),
+                            backing_type=backing_type,
+                            byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
+            backing_type = get_cxx_scalar_type(field.type.width)
+            self.append_(dedent(
+                """
+                if ({field_id}_.has_value()) {{
+                    pdl::packet::Builder::write_{byteorder}<{backing_type}, {size}>(
+                        output, static_cast<{backing_type}>({field_id}_.value()));
+                }}""".format(field_id=field.id,
+                            size=int(field.type.width / 8),
+                            backing_type=backing_type,
+                            byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField):
+            self.append_(dedent(
+                """
+                if ({field_id}_.has_value()) {{
+                    {field_id}_->Serialize(output);
+                }}""".format(field_id=field.id)))
+
+        else:
+            raise Exception(f"unsupported field type {field.__class__.__name__}")
+
     def serialize_payload_field_(self, field: Union[ast.BodyField, ast.PayloadField], var: str):
         """Serialize body and payload fields."""
 
@@ -679,10 +781,13 @@
     def serialize(self, field: ast.Field, decl: ast.Declaration, var: Optional[str] = None):
         field_var = deref(var, f'{field.id}_') if hasattr(field, 'id') else None
 
+        if field.cond:
+            self.serialize_optional_field_(field)
+
         # Field has bit granularity.
         # Append the field to the current chunk,
         # check if a byte boundary was reached.
-        if core.is_bit_field(field):
+        elif core.is_bit_field(field):
             self.serialize_bit_field_(field, var, field_var, decl)
 
         # Padding fields.
@@ -755,10 +860,19 @@
     fields = core.get_unconstrained_parent_fields(decl) + decl.fields
     members = []
     for field in fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)):
+        if field.cond_for:
+            # Scalar fields used as condition for optional fields are treated
+            # as fixed fields since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)):
             members.append("pdl::packet::slice payload_;")
         elif isinstance(field, ast.ArrayField):
             members.append(f"pdl::packet::slice {field.id}_;")
+        elif isinstance(field, ast.ScalarField) and field.cond:
+            members.append(f"std::optional<{get_cxx_scalar_type(field.width)}> {field.id}_{{}};")
+        elif isinstance(field, ast.TypedefField) and field.cond:
+            members.append(f"std::optional<{field.type_id}> {field.id}_{{}};")
         elif isinstance(field, ast.ScalarField):
             members.append(f"{get_cxx_scalar_type(field.width)} {field.id}_{{0}};")
         elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
@@ -780,7 +894,12 @@
 
     members = []
     for field in decl.fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)) and not decl.parent:
+        if field.cond_for:
+            # Scalar fields used as condition for optional fields are treated
+            # as fixed fields since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)) and not decl.parent:
             members.append("std::vector<uint8_t> payload_;")
         elif isinstance(field, ast.ArrayField) and field.size:
             element_type = field.type_id or get_cxx_scalar_type(field.width)
@@ -788,6 +907,10 @@
         elif isinstance(field, ast.ArrayField):
             element_type = field.type_id or get_cxx_scalar_type(field.width)
             members.append(f"std::vector<{element_type}> {field.id}_;")
+        elif isinstance(field, ast.ScalarField) and field.cond:
+            members.append(f"std::optional<{get_cxx_scalar_type(field.width)}> {field.id}_{{}};")
+        elif isinstance(field, ast.TypedefField) and field.cond:
+            members.append(f"std::optional<{field.type_id}> {field.id}_{{}};")
         elif isinstance(field, ast.ScalarField):
             members.append(f"{get_cxx_scalar_type(field.width)} {field.id}_{{0}};")
         elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
@@ -965,7 +1088,16 @@
     variable_width = []
     for f in core.get_packet_fields(decl):
         field_size = core.get_field_size(f)
-        if field_size is not None:
+        if f.cond:
+            if isinstance(f, ast.ScalarField):
+                variable_width.append(f"({f.id}_.has_value() ? {f.width} : 0)")
+            elif isinstance(f, ast.TypedefField) and isinstance(f.type, ast.EnumDeclaration):
+                variable_width.append(f"({f.id}_.has_value() ? {f.type.width} : 0)")
+            elif isinstance(f, ast.TypedefField):
+                variable_width.append(f"({f.id}_.has_value() ? {f.id}_->GetSize() : 0)")
+            else:
+                raise Exception(f"unsupported field type {f.__class__.__name__}")
+        elif field_size is not None:
             constant_width += field_size
         elif isinstance(f, (ast.PayloadField, ast.BodyField)):
             variable_width.append("payload_.size()")
@@ -999,7 +1131,12 @@
     # Add accessors for the backed fields.
     fields = core.get_unconstrained_parent_fields(packet) + packet.fields
     for field in fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)):
+        if field.cond_for:
+            # Scalar fields used as condition for optional fields are treated
+            # as fixed fields since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)):
             accessors.append(
                 dedent("""\
                 std::vector<uint8_t> GetPayload() const {
@@ -1024,6 +1161,7 @@
                             accessor=indent(generate_array_field_accessor(field), 1)))
         elif isinstance(field, ast.ScalarField):
             field_type = get_cxx_scalar_type(field.width)
+            field_type = f"std::optional<{field_type}>" if field.cond else field_type
             accessor_name = to_pascal_case(field.id)
             accessors.append(
                 dedent("""\
@@ -1035,6 +1173,7 @@
                 """).format(field_type=field_type, accessor_name=accessor_name, member_name=field.id))
         elif isinstance(field, ast.TypedefField):
             field_qualifier = "" if isinstance(field.type, ast.EnumDeclaration) else " const&"
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
             accessor_name = to_pascal_case(field.id)
             accessors.append(
                 dedent("""\
@@ -1043,7 +1182,7 @@
                     return {member_name}_;
                 }}
 
-                """).format(field_type=field.type_id,
+                """).format(field_type=field_type,
                             field_qualifier=field_qualifier,
                             accessor_name=accessor_name,
                             member_name=field.id))
@@ -1238,7 +1377,9 @@
             constructor_params.append(f"{field.type_id} {field.id}")
 
     for field in struct.fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)):
+        if field.cond_for:
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)):
             constructor_params.append("std::vector<uint8_t> payload")
             if struct.parent:
                 payload_initializer = f"payload_ = std::move(payload);"
@@ -1254,13 +1395,16 @@
             constructor_initializers.append(f"{field.id}_(std::move({field.id}))")
         elif isinstance(field, ast.ScalarField):
             backing_type = get_cxx_scalar_type(field.width)
-            constructor_params.append(f"{backing_type} {field.id}")
+            field_type = f"std::optional<{backing_type}>" if field.cond else backing_type
+            constructor_params.append(f"{field_type} {field.id}")
             constructor_initializers.append(f"{field.id}_({field.id})")
         elif (isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration)):
-            constructor_params.append(f"{field.type_id} {field.id}")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            constructor_params.append(f"{field_type} {field.id}")
             constructor_initializers.append(f"{field.id}_({field.id})")
         elif isinstance(field, ast.TypedefField):
-            constructor_params.append(f"{field.type_id} {field.id}")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            constructor_params.append(f"{field_type} {field.id}")
             constructor_initializers.append(f"{field.id}_(std::move({field.id}))")
 
     if not constructor_params:
@@ -1271,7 +1415,9 @@
         parent_constructor_params = []
         for field in fields:
             constraints = [c for c in struct.constraints if c.id == getattr(field, 'id', None)]
-            if isinstance(field, (ast.PayloadField, ast.BodyField)):
+            if field.cond_for:
+                pass
+            elif isinstance(field, (ast.PayloadField, ast.BodyField)):
                 parent_constructor_params.append("std::vector<uint8_t>{}")
             elif isinstance(field, ast.ArrayField):
                 parent_constructor_params.append(f"std::move({field.id})")
@@ -1313,7 +1459,12 @@
     fields = core.get_unconstrained_parent_fields(packet) + packet.fields
 
     for field in fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)):
+        if field.cond_for:
+            # Scalar fields used as condition for optional fields are treated
+            # as fixed fields since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)):
             creator_params.append("std::vector<uint8_t> payload")
             constructor_params.append("std::move(payload)")
         elif isinstance(field, ast.ArrayField) and field.size:
@@ -1326,13 +1477,16 @@
             constructor_params.append(f"std::move({field.id})")
         elif isinstance(field, ast.ScalarField):
             backing_type = get_cxx_scalar_type(field.width)
-            creator_params.append(f"{backing_type} {field.id}")
+            field_type = f"std::optional<{backing_type}>" if field.cond else backing_type
+            creator_params.append(f"{field_type} {field.id}")
             constructor_params.append(f"{field.id}")
         elif (isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration)):
-            creator_params.append(f"{field.type_id} {field.id}")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            creator_params.append(f"{field_type} {field.id}")
             constructor_params.append(f"{field.id}")
         elif isinstance(field, ast.TypedefField):
-            creator_params.append(f"{field.type_id} {field.id}")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            creator_params.append(f"{field_type} {field.id}")
             constructor_params.append(f"std::move({field.id})")
 
     creator_params = ', '.join(creator_params)
@@ -1401,7 +1555,12 @@
     post_processing = []
 
     for field in struct.fields:
-        if isinstance(field, (ast.PayloadField, ast.BodyField)):
+        if field.cond_for:
+            # Scalar fields used as condition for optional fields are treated
+            # as fixed fields since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(field, (ast.PayloadField, ast.BodyField)):
             code.append("std::vector<uint8_t> payload_;")
             parsed_fields.append("std::move(payload_)")
         elif isinstance(field, ast.ArrayField) and field.size:
@@ -1414,13 +1573,16 @@
             parsed_fields.append(f"std::move({field.id}_)")
         elif isinstance(field, ast.ScalarField):
             backing_type = get_cxx_scalar_type(field.width)
-            code.append(f"{backing_type} {field.id}_;")
+            field_type = f"std::optional<{backing_type}>" if field.cond else backing_type
+            code.append(f"{field_type} {field.id}_;")
             parsed_fields.append(f"{field.id}_")
         elif (isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration)):
-            code.append(f"{field.type_id} {field.id}_;")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            code.append(f"{field_type} {field.id}_;")
             parsed_fields.append(f"{field.id}_")
         elif isinstance(field, ast.TypedefField):
-            code.append(f"{field.type_id} {field.id}_;")
+            field_type = f"std::optional<{field.type_id}>" if field.cond else field.type_id
+            code.append(f"{field_type} {field.id}_;")
             parsed_fields.append(f"std::move({field.id}_)")
         elif isinstance(field, ast.SizeField):
             code.append(f"{get_cxx_scalar_type(field.width)} {field.field_id}_size;")
@@ -1534,6 +1696,7 @@
 
         #include <cstdint>
         #include <string>
+        #include <optional>
         #include <utility>
         #include <vector>
 
diff --git a/scripts/generate_cxx_backend_tests.py b/scripts/generate_cxx_backend_tests.py
index 880988e..818d376 100755
--- a/scripts/generate_cxx_backend_tests.py
+++ b/scripts/generate_cxx_backend_tests.py
@@ -62,13 +62,30 @@
             sanitized_var = var.replace('[', '_').replace(']', '')
             field_var = f'{sanitized_var}_{id}'
 
-            if isinstance(field, ast.ScalarField):
+            if isinstance(field, ast.ScalarField) and field.cond:
+                value = f"std::make_optional({value})" if value is not None else "std::nullopt"
+                checks.append(f"ASSERT_EQ({get_field(decl, var, id)}, {value});")
+
+            elif isinstance(field, ast.ScalarField):
+                checks.append(f"ASSERT_EQ({get_field(decl, var, id)}, {value});")
+
+            elif (isinstance(field, ast.TypedefField) and
+                  isinstance(field.type, ast.EnumDeclaration) and
+                  field.cond):
+                value = f"std::make_optional({field.type_id}({value}))" if value is not None else "std::nullopt"
                 checks.append(f"ASSERT_EQ({get_field(decl, var, id)}, {value});")
 
             elif (isinstance(field, ast.TypedefField) and
                   isinstance(field.type, (ast.EnumDeclaration, ast.CustomFieldDeclaration, ast.ChecksumDeclaration))):
                 checks.append(f"ASSERT_EQ({get_field(decl, var, id)}, {field.type_id}({value}));")
 
+            elif isinstance(field, ast.TypedefField) and field.cond and value is None:
+                checks.append(f"ASSERT_TRUE(!{get_field(decl, var, id)}.has_value());")
+
+            elif isinstance(field, ast.TypedefField) and field.cond and value is not None:
+                checks.append(f"{field.type_id} const& {field_var} = {get_field(decl, var, id)}.value();")
+                checks.extend(check_members(field.type, field_var, value))
+
             elif isinstance(field, ast.TypedefField):
                 checks.append(f"{field.type_id} const& {field_var} = {get_field(decl, var, id)};")
                 checks.extend(check_members(field.type, field_var, value))
@@ -169,9 +186,21 @@
             value = initializer['payload'] if isinstance(field, (ast.PayloadField,
                                                                  ast.BodyField)) else initializer.get(field_id, None)
 
-            if isinstance(field, ast.ScalarField):
+            if field.cond_for:
+                pass
+
+            elif field.cond and value is None:
+                parameters.append("std::nullopt")
+
+            elif isinstance(field, ast.ScalarField) and field.cond:
+                parameters.append(f"std::make_optional({value})")
+
+            elif isinstance(field, ast.ScalarField):
                 parameters.append(f"{value}")
 
+            elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration) and field.cond:
+                parameters.append(f"std::make_optional({field.type_id}({value}))")
+
             elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
                 parameters.append(f"{field.type_id}({value})")
 
diff --git a/scripts/generate_python_backend.py b/scripts/generate_python_backend.py
index 475154d..8938173 100755
--- a/scripts/generate_python_backend.py
+++ b/scripts/generate_python_backend.py
@@ -130,11 +130,11 @@
         after parsing is completed."""
         self.unchecked_code.append(line)
 
-    def append_(self, line: str):
+    def append_(self, code: str):
         """Append field parsing code.
         There must be no unchecked code left before this function is called."""
         assert len(self.unchecked_code) == 0
-        self.code.append(line)
+        self.code.extend(code.split('\n'))
 
     def check_size_(self, size: str):
         """Generate a check of the current span size."""
@@ -318,6 +318,60 @@
         if padded_size:
             self.append_(f"span = remaining_span")
 
+    def parse_optional_field_(self, field: ast.Field):
+        """Parse the selected optional field.
+        Optional fields must start and end on a byte boundary."""
+
+        if self.shift != 0:
+            raise Exception('Optional field does not start on an octet boundary')
+        if (isinstance(field, ast.TypedefField) and
+            isinstance(field.type, ast.StructDeclaration) and
+            field.type.parent_id is not None):
+            raise Exception('Derived struct used in optional typedef field')
+
+        self.consume_span_()
+
+        if isinstance(field, ast.ScalarField):
+            self.append_(dedent("""
+            if {cond_id} == {cond_value}:
+                if len(span) < {size}:
+                    raise Exception('Invalid packet size')
+                fields['{field_id}'] = int.from_bytes(span[:{size}], byteorder='{byteorder}')
+                span = span[{size}:]
+            """.format(size=int(field.width / 8),
+                       field_id=field.id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value,
+                       byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
+            self.append_(dedent("""
+            if {cond_id} == {cond_value}:
+                if len(span) < {size}:
+                    raise Exception('Invalid packet size')
+                fields['{field_id}'] = {type_id}(
+                    int.from_bytes(span[:{size}], byteorder='{byteorder}'))
+                span = span[{size}:]
+            """.format(size=int(field.type.width / 8),
+                       field_id=field.id,
+                       type_id=field.type_id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value,
+                       byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField):
+            self.append_(dedent("""
+            if {cond_id} == {cond_value}:
+                {field_id}, span = {type_id}.parse(span)
+                fields['{field_id}'] = {field_id}
+            """.format(field_id=field.id,
+                       type_id=field.type_id,
+                       cond_id=field.cond.id,
+                       cond_value=field.cond.value)))
+
+        else:
+            raise Exception(f"unsupported field type {field.__class__.__name__}")
+
     def parse_bit_field_(self, field: ast.Field):
         """Parse the selected field as a bit field.
         The field is added to the current chunk. When a byte boundary
@@ -347,7 +401,9 @@
         for shift, width, field in self.chunk:
             v = (value if len(self.chunk) == 1 and shift == 0 else f"({value} >> {shift}) & {mask(width)}")
 
-            if isinstance(field, ast.ScalarField):
+            if field.cond_for:
+                self.unchecked_append_(f"{field.id} = {v}")
+            elif isinstance(field, ast.ScalarField):
                 self.unchecked_append_(f"fields['{field.id}'] = {v}")
             elif isinstance(field, ast.FixedField) and field.enum_id:
                 self.unchecked_append_(f"if {v} != {field.enum_id}.{field.tag_id}:")
@@ -511,10 +567,13 @@
                      f" {{computed_{value_field.id}}} != {{{value_field.id}}}')")
 
     def parse(self, field: ast.Field):
+        if field.cond:
+            self.parse_optional_field_(field)
+
         # Field has bit granularity.
         # Append the field to the current chunk,
         # check if a byte boundary was reached.
-        if core.is_bit_field(field):
+        elif core.is_bit_field(field):
             self.parse_bit_field_(field)
 
         # Padding fields.
@@ -600,6 +659,35 @@
         if field.padded_size:
             self.append_(f"_span.extend([0] * ({field.padded_size} - len(_span) + _{field.id}_start))")
 
+    def serialize_optional_field_(self, field: ast.Field):
+        if isinstance(field, ast.ScalarField):
+            self.append_(dedent(
+                """
+                if self.{field_id} is not None:
+                    _span.extend(int.to_bytes(self.{field_id}, length={size}, byteorder='{byteorder}'))
+                """.format(field_id=field.id,
+                            size=int(field.width / 8),
+                            byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField) and isinstance(field.type, ast.EnumDeclaration):
+            self.append_(dedent(
+                """
+                if self.{field_id} is not None:
+                    _span.extend(int.to_bytes(self.{field_id}, length={size}, byteorder='{byteorder}'))
+                """.format(field_id=field.id,
+                            size=int(field.type.width / 8),
+                            byteorder=self.byteorder)))
+
+        elif isinstance(field, ast.TypedefField):
+            self.append_(dedent(
+                """
+                if self.{field_id} is not None:
+                    _span.extend(self.{field_id}.serialize())
+                """.format(field_id=field.id)))
+
+        else:
+            raise Exception(f"unsupported field type {field.__class__.__name__}")
+
     def serialize_bit_field_(self, field: ast.Field):
         """Serialize the selected field as a bit field.
         The field is added to the current chunk. When a byte boundary
@@ -611,6 +699,13 @@
 
         if isinstance(field, str):
             self.value.append(f"({field} << {shift})")
+        elif field.cond_for:
+            # Scalar field used as condition for an optional field.
+            # The width is always 1, the value is determined from
+            # the presence or absence of the optional field.
+            value_present = field.cond_for.cond.value
+            value_absent = 0 if field.cond_for.cond.value else 1
+            self.value.append(f"(({value_absent} if self.{field.cond_for.id} is None else {value_present}) << {shift})")
         elif isinstance(field, ast.ScalarField):
             max_value = (1 << field.width) - 1
             self.append_(f"if self.{field.id} > {max_value}:")
@@ -741,10 +836,13 @@
         self.append_("_checksum_start = len(_span)")
 
     def serialize(self, field: ast.Field):
+        if field.cond:
+            self.serialize_optional_field_(field)
+
         # Field has bit granularity.
         # Append the field to the current chunk,
         # check if a byte boundary was reached.
-        if core.is_bit_field(field):
+        elif core.is_bit_field(field):
             self.serialize_bit_field_(field)
 
         # Padding fields.
@@ -806,9 +904,10 @@
 
     # Convert the packet constraints to a boolean expression.
     validation = []
-    if packet.constraints:
+    constraints = core.get_all_packet_constraints(packet)
+    if constraints:
         cond = []
-        for c in packet.constraints:
+        for c in constraints:
             if c.value is not None:
                 cond.append(f"fields['{c.id}'] != {hex(c.value)}")
             else:
@@ -848,7 +947,16 @@
     variable_width = []
     for f in packet.fields:
         field_size = core.get_field_size(f)
-        if field_size is not None:
+        if f.cond:
+            if isinstance(f, ast.ScalarField):
+                return f"(0 if self.{f.id} is None else {f.width})"
+            elif isinstance(f, ast.TypedefField) and isinstance(f.type, ast.EnumDeclaration):
+                return f"(0 if self.{f.id} is None else {f.type.width})"
+            elif isinstance(f, ast.TypedefField):
+                return f"(0 if self.{f.id} is None else self.{f.id}.size)"
+            else:
+                raise Exception(f"unsupported field type {f.__class__.__name__}")
+        elif field_size is not None:
             constant_width += field_size
         elif isinstance(f, (ast.PayloadField, ast.BodyField)):
             variable_width.append("len(self.payload)")
@@ -882,11 +990,7 @@
     """Generate __post_init__ function to set constraint field values."""
 
     # Gather all constraints from parent packets.
-    constraints = []
-    current = decl
-    while current.parent_id:
-        constraints.extend(current.constraints)
-        current = current.parent
+    constraints = core.get_all_packet_constraints(decl)
 
     if constraints:
         code = []
@@ -928,7 +1032,19 @@
     packet_name = packet.id
     field_decls = []
     for f in packet.fields:
-        if isinstance(f, ast.ScalarField):
+        if f.cond:
+            if isinstance(f, ast.ScalarField):
+                field_decls.append(f"{f.id}: Optional[int] = field(kw_only=True, default=None)")
+            elif isinstance(f, ast.TypedefField):
+                field_decls.append(f"{f.id}: Optional[{f.type_id}] = field(kw_only=True, default=None)")
+            else:
+                pass
+        elif f.cond_for:
+            # The fields used as condition for optional fields are
+            # not generated since their value is tied to the value of the
+            # optional field.
+            pass
+        elif isinstance(f, ast.ScalarField):
             field_decls.append(f"{f.id}: int = field(kw_only=True, default=0)")
         elif isinstance(f, ast.TypedefField):
             if isinstance(f.type, ast.EnumDeclaration):
diff --git a/scripts/generate_test_vectors.py b/scripts/generate_test_vectors.py
index ef2ea24..3997da3 100755
--- a/scripts/generate_test_vectors.py
+++ b/scripts/generate_test_vectors.py
@@ -70,6 +70,8 @@
                 v.serialize_(serializer)
         elif isinstance(self.value, Packet):
             self.value.serialize_(serializer)
+        elif self.value == None:
+            pass
         else:
             raise Exception(f"Malformed value {self.value}")
 
@@ -179,6 +181,18 @@
 generator = BitGenerator()
 
 
+def generate_cond_field_values(field: ast.ScalarField) -> List[Value]:
+    cond_value_present = field.cond_for.cond.value
+    cond_value_absent = 0 if field.cond_for.cond.value != 0 else 1
+
+    def get_cond_value(parent: Packet, field: ast.Field) -> int:
+        for f in parent.fields:
+            if f.ref is field:
+                return cond_value_absent if f.value.value is None else cond_value_present
+
+    return [Value(lambda p: get_cond_value(p, field.cond_for), field.width)]
+
+
 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:
@@ -489,7 +503,10 @@
 def generate_field_values(
     field: ast.Field, constraints: List[ast.Constraint], payload: Optional[List[Packet]]
 ) -> List[Value]:
-    if isinstance(field, ast.ChecksumField):
+    if field.cond_for:
+        return generate_cond_field_values(field)
+
+    elif isinstance(field, ast.ChecksumField):
         # Checksum fields are just markers.
         return [Value(0, 0)]
 
@@ -547,10 +564,12 @@
     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
-    ]
+    fields = []
+    for f in decl.fields:
+        values = generate_field_values(f, constraints, payload)
+        optional_none = [] if not f.cond else [Field(Value(None, 0), f)]
+        fields.append(optional_none + [Field(v, f) for v in values])
+    return fields
 
 
 def generate_fields_recursive(
diff --git a/scripts/pdl/ast.py b/scripts/pdl/ast.py
index b07f6ad..5ade0fa 100644
--- a/scripts/pdl/ast.py
+++ b/scripts/pdl/ast.py
@@ -66,7 +66,10 @@
 @dataclass
 class Field(Node):
     parent: Node = field(init=False)
-
+    cond: Optional[Constraint] = field(init=False, default=None)
+    # Backlink to the (optional) optional field referencing
+    # this field as condition.
+    cond_for: Optional['Field'] = field(init=False, default=None)
 
 @node('checksum_field')
 class ChecksumField(Field):
@@ -274,8 +277,14 @@
         loc = SourceRange(loc['file'], SourceLocation(**loc['start']), SourceLocation(**loc['end']))
         constructor = constructors_.get(kind)
         members = {'loc': loc, 'kind': kind}
+        cond = None
         for name, value in obj.items():
-            if name != 'kind' and name != 'loc':
+            if name == 'cond':
+                cond = convert_(value)
+            elif name != 'kind' and name != 'loc':
                 members[name] = convert_(value)
-        return constructor(**members)
+        val = constructor(**members)
+        if cond:
+            val.cond = cond
+        return val
     raise Exception('Unhandled json object type')
diff --git a/scripts/pdl/core.py b/scripts/pdl/core.py
index f55bb30..5ddbb02 100644
--- a/scripts/pdl/core.py
+++ b/scripts/pdl/core.py
@@ -68,6 +68,15 @@
                 fields.extend(desugar_field_(f, fields[-1] if len(fields) > 0 else None, {}))
             d.fields = fields
 
+            # Add backlinks from optional links to their flag scalar
+            # field declaration.
+            fields_by_id = dict()
+            for f in d.fields:
+                if hasattr(f, 'id'):
+                    fields_by_id[f.id] = f
+                if f.cond:
+                    fields_by_id[f.cond.id].cond_for = f
+
         declarations.append(d)
 
     file.declarations = declarations
@@ -173,13 +182,29 @@
     return children
 
 
+def get_all_packet_constraints(
+    decl: Union[PacketDeclaration, StructDeclaration]
+) -> List[Constraint]:
+    """Return the list of constraints defined in the selected packet and
+    its parent declarations."""
+
+    constraints = []
+    while decl.parent_id:
+        constraints.extend(decl.constraints)
+        decl = decl.parent
+    return constraints
+
+
 def get_field_size(field: Field, skip_payload: bool = False) -> Optional[int]:
     """Determine the size of a field in bits, if possible.
     If the field is dynamically sized (e.g. unsized array or payload field),
     None is returned instead. If skip_payload is set, payload and body fields
     are counted as having size 0 rather than a variable size."""
 
-    if isinstance(field, (ScalarField, SizeField, CountField, ReservedField)):
+    if field.cond:
+        return None
+
+    elif isinstance(field, (ScalarField, SizeField, CountField, ReservedField)):
         return field.width
 
     elif isinstance(field, FixedField):
@@ -324,7 +349,10 @@
     These include: ScalarField, FixedField, TypedefField with enum type,
     SizeField, and CountField."""
 
-    if isinstance(field, (ScalarField, SizeField, CountField, FixedField, ReservedField)):
+    if field.cond:
+        return False
+
+    elif isinstance(field, (ScalarField, SizeField, CountField, FixedField, ReservedField)):
         return True
 
     elif isinstance(field, TypedefField) and isinstance(field.type, EnumDeclaration):
diff --git a/src/analyzer.rs b/src/analyzer.rs
index d1cf84e..b80a6b6 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -190,6 +190,12 @@
     E42 = 42,
     E43 = 43,
     DuplicateDefaultTag = 44,
+    InvalidOptionalField = 45,
+    UndeclaredConditionIdentifier = 46,
+    InvalidConditionIdentifier = 47,
+    InvalidConditionValue = 48,
+    E49 = 49,
+    ReusedConditionIdentifier = 50,
 }
 
 impl From<ErrorCode> for String {
@@ -351,6 +357,7 @@
             | FieldDesc::FixedScalar { .. }
             | FieldDesc::Reserved { .. }
             | FieldDesc::Group { .. }
+            | FieldDesc::Flag { .. }
             | FieldDesc::Scalar { .. }
             | FieldDesc::Array { type_id: None, .. } => None,
             FieldDesc::FixedEnum { enum_id: type_id, .. }
@@ -368,6 +375,7 @@
             | FieldDesc::FixedScalar { .. }
             | FieldDesc::FixedEnum { .. }
             | FieldDesc::Reserved { .. }
+            | FieldDesc::Flag { .. }
             | FieldDesc::Scalar { .. } => true,
             FieldDesc::Typedef { type_id, .. } => {
                 let field = self.typedef.get(type_id.as_str());
@@ -1405,6 +1413,120 @@
     Ok(())
 }
 
+/// Check optional fields.
+/// Raises error diagnostics for the following cases:
+///      - invalid optional field
+///      - invalid constraint identifier
+///      - invalid constraint scalar value (bad type)
+///      - invalid constraint scalar value (overflow)
+fn check_optional_fields(file: &parser_ast::File) -> Result<(), Diagnostics> {
+    let mut diagnostics: Diagnostics = Default::default();
+    for decl in &file.declarations {
+        let mut local_scope: HashMap<String, &parser_ast::Field> = HashMap::new();
+        let mut condition_ids: HashMap<String, &parser_ast::Field> = HashMap::new();
+        for field in decl.fields() {
+            if let Some(ref cond) = field.cond {
+                match &field.desc {
+                    FieldDesc::Scalar { .. } | FieldDesc::Typedef { .. } => (),
+                    _ => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::InvalidOptionalField)
+                            .with_message("invalid optional field".to_owned())
+                            .with_labels(vec![field.loc.primary()])
+                            .with_notes(vec!["note: expected scalar, or typedef field".to_owned()]),
+                    ),
+                }
+                match local_scope.get(&cond.id) {
+                    None => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::UndeclaredConditionIdentifier)
+                            .with_message("undeclared condition identifier".to_owned())
+                            .with_labels(vec![field.loc.primary()])
+                            .with_notes(vec!["note: expected scalar field identifier".to_owned()]),
+                    ),
+                    Some(Field { cond: Some(_), loc, .. }) => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::E49)
+                            .with_message("invalid condition identifier".to_owned())
+                            .with_labels(vec![
+                                field.loc.primary(),
+                                loc.secondary().with_message(format!(
+                                    "`{}` is declared optional here",
+                                    cond.id
+                                )),
+                            ])
+                            .with_notes(vec!["note: expected scalar field identifier".to_owned()]),
+                    ),
+                    Some(Field { desc: FieldDesc::Scalar { width: 1, .. }, .. }) => (),
+                    Some(Field { desc: FieldDesc::Scalar { width, .. }, loc, .. }) => diagnostics
+                        .push(
+                            Diagnostic::error()
+                                .with_code(ErrorCode::InvalidConditionIdentifier)
+                                .with_message("invalid condition identifier".to_owned())
+                                .with_labels(vec![
+                                    field.loc.primary(),
+                                    loc.secondary().with_message(format!(
+                                        "`{}` is declared with width `{}` here",
+                                        cond.id, width
+                                    )),
+                                ])
+                                .with_notes(vec![
+                                    "note: expected scalar field identifier".to_owned()
+                                ]),
+                        ),
+                    Some(Field { loc, .. }) => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::InvalidConditionIdentifier)
+                            .with_message("invalid condition identifier".to_owned())
+                            .with_labels(vec![
+                                field.loc.primary(),
+                                loc.secondary()
+                                    .with_message(format!("`{}` is declared here", cond.id)),
+                            ])
+                            .with_notes(vec!["note: expected scalar field identifier".to_owned()]),
+                    ),
+                }
+                match (&cond.value, &cond.tag_id) {
+                    (_, Some(_)) => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::InvalidConditionValue)
+                            .with_message("invalid condition value".to_owned())
+                            .with_labels(vec![field.loc.primary()])
+                            .with_notes(vec!["note: expected 0 or 1".to_owned()]),
+                    ),
+                    (Some(0), _) | (Some(1), _) => (),
+                    (Some(_), _) => diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::InvalidConditionValue)
+                            .with_message("invalid condition value".to_owned())
+                            .with_labels(vec![field.loc.primary()])
+                            .with_notes(vec!["note: expected 0 or 1".to_owned()]),
+                    ),
+                    _ => unreachable!(),
+                }
+                if let Some(prev_field) = condition_ids.insert(cond.id.to_owned(), field) {
+                    diagnostics.push(
+                        Diagnostic::error()
+                            .with_code(ErrorCode::ReusedConditionIdentifier)
+                            .with_message("reused condition identifier".to_owned())
+                            .with_labels(vec![
+                                field.loc.primary(),
+                                prev_field
+                                    .loc
+                                    .secondary()
+                                    .with_message(format!("`{}` is first used here", cond.id)),
+                            ]),
+                    )
+                }
+            }
+            if let Some(id) = field.id() {
+                local_scope.insert(id.to_owned(), field);
+            }
+        }
+    }
+    diagnostics.err_or(())
+}
+
 /// Check correct definition of packet sizes.
 /// Annotate fields and declarations with the size in bits.
 fn compute_field_sizes(file: &parser_ast::File) -> ast::File {
@@ -1478,6 +1600,7 @@
         scope: &HashMap<String, ast::DeclAnnotation>,
     ) -> ast::Field {
         field.annotate(match &field.desc {
+            _ if field.cond.is_some() => ast::FieldAnnotation::new(ast::Size::Dynamic),
             FieldDesc::Checksum { .. } | FieldDesc::Padding { .. } => {
                 ast::FieldAnnotation::new(ast::Size::Static(0))
             }
@@ -1489,6 +1612,7 @@
             | FieldDesc::Scalar { width, .. } => {
                 ast::FieldAnnotation::new(ast::Size::Static(*width))
             }
+            FieldDesc::Flag { .. } => ast::FieldAnnotation::new(ast::Size::Static(1)),
             FieldDesc::Body | FieldDesc::Payload { .. } => {
                 let has_payload_size = decl.fields().any(|field| match &field.desc {
                     FieldDesc::Size { field_id, .. } => {
@@ -1583,6 +1707,7 @@
                         },
                         loc: field.loc,
                         annot: field.annot,
+                        cond: field.cond.clone(),
                     }]
                 }
                 FieldDesc::Typedef { id, type_id, .. } if constraints.contains_key(id) => {
@@ -1596,6 +1721,7 @@
                         },
                         loc: field.loc,
                         annot: field.annot,
+                        cond: field.cond.clone(),
                     }]
                 }
                 _ => vec![field.clone()],
@@ -1649,6 +1775,42 @@
     })
 }
 
+/// Replace Scalar fields used as condition for optional fields by the more
+/// specific Flag construct.
+fn desugar_flags(file: &mut parser_ast::File) {
+    for decl in &mut file.declarations {
+        match &mut decl.desc {
+            DeclDesc::Packet { fields, .. }
+            | DeclDesc::Struct { fields, .. }
+            | DeclDesc::Group { fields, .. } => {
+                // Gather information about condition flags.
+                let mut condition_ids: HashMap<String, (String, usize)> = HashMap::new();
+                for field in fields.iter() {
+                    if let Some(ref cond) = field.cond {
+                        condition_ids.insert(
+                            cond.id.to_owned(),
+                            (field.id().unwrap().to_owned(), cond.value.unwrap()),
+                        );
+                    }
+                }
+                // Replace condition flags in the fields.
+                for field in fields.iter_mut() {
+                    if let Some((optional_field_id, set_value)) =
+                        field.id().and_then(|id| condition_ids.get(id))
+                    {
+                        field.desc = FieldDesc::Flag {
+                            id: field.id().unwrap().to_owned(),
+                            optional_field_id: optional_field_id.to_owned(),
+                            set_value: *set_value,
+                        }
+                    }
+                }
+            }
+            _ => (),
+        }
+    }
+}
+
 /// Analyzer entry point, produces a new AST with annotations resulting
 /// from the analysis.
 pub fn analyze(file: &parser_ast::File) -> Result<ast::File, Diagnostics> {
@@ -1662,8 +1824,10 @@
     check_array_fields(file)?;
     check_padding_fields(file)?;
     check_checksum_fields(file, &scope)?;
+    check_optional_fields(file)?;
     check_group_constraints(file, &scope)?;
-    let file = inline_groups(file)?;
+    let mut file = inline_groups(file)?;
+    desugar_flags(&mut file);
     let scope = Scope::new(&file)?;
     check_decl_constraints(&file, &scope)?;
     Ok(compute_field_sizes(&file))
@@ -1681,8 +1845,7 @@
     macro_rules! raises {
         ($code:ident, $text:literal) => {{
             let mut db = SourceDatabase::new();
-            let file = parse_inline(&mut db, "stdin".to_owned(), $text.to_owned())
-                .expect("parsing failure");
+            let file = parse_inline(&mut db, "stdin", $text.to_owned()).expect("parsing failure");
             let result = analyzer::analyze(&file);
             assert!(matches!(result, Err(_)));
             let diagnostics = result.err().unwrap();
@@ -1697,8 +1860,7 @@
     macro_rules! valid {
         ($text:literal) => {{
             let mut db = SourceDatabase::new();
-            let file = parse_inline(&mut db, "stdin".to_owned(), $text.to_owned())
-                .expect("parsing failure");
+            let file = parse_inline(&mut db, "stdin", $text.to_owned()).expect("parsing failure");
             assert!(analyzer::analyze(&file).is_ok());
         }};
     }
@@ -2691,6 +2853,218 @@
     }
 
     #[test]
+    fn test_e45() {
+        valid!(
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8 if c = 1,
+        }
+        "#
+        );
+
+        valid!(
+            r#"
+        little_endian_packets
+        enum A : 8 { X = 0 }
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : A if c = 0,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8[] if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        packet A {
+            c : 1,
+            _reserved_ : 7,
+            _size_(x) : 8 if c = 1,
+            x : 8[],
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8[],
+            _padding_ [10] if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            _reserved_ : 8 if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            _fixed_ = 0x42 : 8 if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidOptionalField,
+            r#"
+        little_endian_packets
+        enum A : 8 { X = 0 }
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            _fixed_ = X : A if c = 1,
+        }
+        "#
+        );
+    }
+
+    #[test]
+    fn test_e46() {
+        raises!(
+            UndeclaredConditionIdentifier,
+            r#"
+        little_endian_packets
+        packet B {
+            x : 8 if c = 1,
+            _reserved_ : 7,
+        }
+        "#
+        );
+    }
+
+    #[test]
+    fn test_e47() {
+        raises!(
+            InvalidConditionIdentifier,
+            r#"
+        little_endian_packets
+        enum A : 8 { X = 0 }
+        packet B {
+            c : A,
+            x : 8 if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidConditionIdentifier,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 8[],
+            x : 8 if c = 1,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidConditionIdentifier,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 8,
+            x : 8 if c = 1,
+        }
+        "#
+        );
+    }
+
+    #[test]
+    fn test_e48() {
+        raises!(
+            InvalidConditionValue,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8 if c = A,
+        }
+        "#
+        );
+
+        raises!(
+            InvalidConditionValue,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8 if c = 2,
+        }
+        "#
+        );
+    }
+
+    #[test]
+    fn test_e49() {
+        raises!(
+            E49,
+            r#"
+        little_endian_packets
+        packet B {
+            c0 : 1,
+            _reserved_ : 7,
+            c1 : 1 if c0 = 1,
+            _reserved_ : 7,
+            x : 8 if c1 = 1,
+        }
+        "#
+        );
+    }
+
+    #[test]
+    fn test_e50() {
+        raises!(
+            ReusedConditionIdentifier,
+            r#"
+        little_endian_packets
+        packet B {
+            c : 1,
+            _reserved_ : 7,
+            x : 8 if c = 1,
+            y : 8 if c = 0,
+        }
+        "#
+        );
+    }
+
+    #[test]
     fn test_enum_declaration() {
         valid!(
             r#"
@@ -2750,8 +3124,7 @@
 
     fn annotations(text: &str) -> Vec<Annotations> {
         let mut db = SourceDatabase::new();
-        let file =
-            parse_inline(&mut db, "stdin".to_owned(), text.to_owned()).expect("parsing failure");
+        let file = parse_inline(&mut db, "stdin", text.to_owned()).expect("parsing failure");
         let file = analyzer::analyze(&file).expect("analyzer failure");
         file.declarations
             .iter()
@@ -3255,8 +3628,7 @@
 
     fn desugar(text: &str) -> analyzer::ast::File {
         let mut db = SourceDatabase::new();
-        let file =
-            parse_inline(&mut db, "stdin".to_owned(), text.to_owned()).expect("parsing failure");
+        let file = parse_inline(&mut db, "stdin", text.to_owned()).expect("parsing failure");
         analyzer::analyze(&file).expect("analyzer failure")
     }
 
diff --git a/src/ast.rs b/src/ast.rs
index 57b23fa..20d8bb8 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -18,7 +18,7 @@
 use std::fmt;
 use std::ops;
 
-/// File identfiier.
+/// File identifier.
 /// References a source file in the source database.
 pub type FileId = usize;
 
@@ -82,7 +82,7 @@
 pub struct TagRange {
     pub id: String,
     pub loc: SourceRange,
-    pub range: std::ops::RangeInclusive<usize>,
+    pub range: ops::RangeInclusive<usize>,
     pub tags: Vec<TagValue>,
 }
 
@@ -143,6 +143,10 @@
     },
     #[serde(rename = "scalar_field")]
     Scalar { id: String, width: usize },
+    /// Special case of Scalar for fields used as condition for
+    /// optional fields. The width is always 1.
+    #[serde(rename = "flag_field")]
+    Flag { id: String, optional_field_id: String, set_value: usize },
     #[serde(rename = "typedef_field")]
     Typedef { id: String, type_id: String },
     #[serde(rename = "group_field")]
@@ -156,6 +160,7 @@
     pub annot: A::FieldAnnotation,
     #[serde(flatten)]
     pub desc: FieldDesc,
+    pub cond: Option<Constraint>,
 }
 
 #[derive(Debug, Serialize, Clone)]
@@ -263,7 +268,7 @@
     type Output = SourceRange;
 
     fn add(self, rhs: SourceRange) -> SourceRange {
-        assert!(self.file == rhs.file);
+        assert_eq!(self.file, rhs.file);
         SourceRange {
             file: self.file,
             start: self.start.min(rhs.start),
@@ -275,7 +280,7 @@
 impl Eq for Endianness {}
 impl PartialEq for Endianness {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc.
+        // Implement structural equality, leave out loc.
         self.value == other.value
     }
 }
@@ -283,7 +288,7 @@
 impl Eq for TagValue {}
 impl PartialEq for TagValue {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc.
+        // Implement structural equality, leave out loc.
         self.id == other.id && self.value == other.value
     }
 }
@@ -291,7 +296,7 @@
 impl Eq for TagRange {}
 impl PartialEq for TagRange {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc.
+        // Implement structural equality, leave out loc.
         self.id == other.id && self.range == other.range && self.tags == other.tags
     }
 }
@@ -332,7 +337,7 @@
 impl Eq for Constraint {}
 impl PartialEq for Constraint {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc.
+        // Implement structural equality, leave out loc.
         self.id == other.id && self.value == other.value && self.tag_id == other.tag_id
     }
 }
@@ -340,15 +345,15 @@
 impl Eq for TestCase {}
 impl PartialEq for TestCase {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc.
+        // Implement structural equality, leave out loc.
         self.input == other.input
     }
 }
 
-impl<A: Annotation + std::cmp::PartialEq> Eq for File<A> {}
-impl<A: Annotation + std::cmp::PartialEq> PartialEq for File<A> {
+impl<A: Annotation + PartialEq> Eq for File<A> {}
+impl<A: Annotation + PartialEq> PartialEq for File<A> {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out comments and PDL
+        // Implement structural equality, leave out comments and PDL
         // version information.
         self.endianness == other.endianness && self.declarations == other.declarations
     }
@@ -378,10 +383,10 @@
     }
 }
 
-impl<A: Annotation + std::cmp::PartialEq> Eq for Decl<A> {}
-impl<A: Annotation + std::cmp::PartialEq> PartialEq for Decl<A> {
+impl<A: Annotation + PartialEq> Eq for Decl<A> {}
+impl<A: Annotation + PartialEq> PartialEq for Decl<A> {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc and annot.
+        // Implement structural equality, leave out loc and annot.
         self.desc == other.desc
     }
 }
@@ -512,14 +517,14 @@
 impl<A: Annotation> Eq for Field<A> {}
 impl<A: Annotation> PartialEq for Field<A> {
     fn eq(&self, other: &Self) -> bool {
-        // Implement structual equality, leave out loc and annot.
+        // Implement structural equality, leave out loc and annot.
         self.desc == other.desc
     }
 }
 
 impl<A: Annotation> Field<A> {
     pub fn annotate<B: Annotation>(&self, annot: B::FieldAnnotation) -> Field<B> {
-        Field { loc: self.loc, annot, desc: self.desc.clone() }
+        Field { loc: self.loc, annot, cond: self.cond.clone(), desc: self.desc.clone() }
     }
 
     pub fn id(&self) -> Option<&str> {
@@ -537,6 +542,7 @@
             | FieldDesc::Group { .. } => None,
             FieldDesc::Array { id, .. }
             | FieldDesc::Scalar { id, .. }
+            | FieldDesc::Flag { id, .. }
             | FieldDesc::Typedef { id, .. } => Some(id),
         }
     }
@@ -555,6 +561,7 @@
             FieldDesc::Group { .. } => "group",
             FieldDesc::Array { .. } => "array",
             FieldDesc::Scalar { .. } => "scalar",
+            FieldDesc::Flag { .. } => "scalar",
             FieldDesc::Typedef { .. } => "typedef",
         }
     }
diff --git a/src/backends/intermediate.rs b/src/backends/intermediate.rs
index e0d1041..9c3c231 100644
--- a/src/backends/intermediate.rs
+++ b/src/backends/intermediate.rs
@@ -202,6 +202,7 @@
                 );
                 ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64)
             }
+            ast::FieldDesc::Flag { .. } => unimplemented!(),
             ast::FieldDesc::Group { .. } => {
                 unimplemented!("this should be removed by the linter...")
             }
diff --git a/src/backends/json.rs b/src/backends/json.rs
index 460d72c..526b29e 100644
--- a/src/backends/json.rs
+++ b/src/backends/json.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Rust compiler backend.
+//! Json compiler backend.
 
 use crate::parser;
 
diff --git a/src/backends/rust.rs b/src/backends/rust.rs
index 9dfdf8a..5a6c921 100644
--- a/src/backends/rust.rs
+++ b/src/backends/rust.rs
@@ -111,6 +111,30 @@
                     }
                 }
             }
+            ast::FieldDesc::Scalar { id, width } => {
+                assert!(field.cond.is_some());
+                let id = id.to_ident();
+                let width = syn::Index::from(*width / 8);
+                quote!(if self.#id.is_some() { #width } else { 0 })
+            }
+            ast::FieldDesc::Typedef { id, type_id, .. } if field.cond.is_some() => {
+                let id = id.to_ident();
+                match &scope.typedef[type_id].desc {
+                    ast::DeclDesc::Enum { width, .. } => {
+                        let width = syn::Index::from(*width / 8);
+                        quote!(if self.#id.is_some() { #width } else { 0 })
+                    }
+                    _ => {
+                        let type_id = type_id.to_ident();
+                        quote! {
+                            self.#id
+                                .as_ref()
+                                .map(#type_id::get_size)
+                                .unwrap_or(0)
+                        }
+                    }
+                }
+            }
             ast::FieldDesc::Typedef { id, .. } => {
                 let id = id.to_ident();
                 quote!(self.#id.get_size())
@@ -259,10 +283,15 @@
     let has_children = scope.iter_children(decl).next().is_some();
 
     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 backed_fields = decl
+        .fields()
+        .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
+        .collect::<Vec<_>>();
+
     let mut field_names =
-        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<_>>();
+        backed_fields.iter().map(|f| f.id().unwrap().to_ident()).collect::<Vec<_>>();
+    let mut field_types = backed_fields.iter().map(|f| types::rust_type(f)).collect::<Vec<_>>();
+
     if has_children || has_payload {
         if is_packet {
             field_names.push(format_ident!("child"));
@@ -383,7 +412,10 @@
     let parent_data_child = parent_ids.iter().map(|id| format_ident!("{id}DataChild"));
 
     let all_fields = {
-        let mut fields = scope.iter_fields(decl).filter(|d| d.id().is_some()).collect::<Vec<_>>();
+        let mut fields = scope
+            .iter_fields(decl)
+            .filter(|f| f.id().is_some() && !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
+            .collect::<Vec<_>>();
         fields.sort_by_key(|f| f.id());
         fields
     };
@@ -425,7 +457,11 @@
         let parent_data_child = format_ident!("{parent_id}DataChild");
 
         let named_fields = {
-            let mut names = parent.fields().filter_map(ast::Field::id).collect::<Vec<_>>();
+            let mut names = parent
+                .fields()
+                .filter(|f| !matches!(&f.desc, ast::FieldDesc::Flag { .. }))
+                .filter_map(ast::Field::id)
+                .collect::<Vec<_>>();
             names.sort_unstable();
             names
         };
@@ -985,18 +1021,28 @@
 ///
 /// The code is not formatted, pipe it through `rustfmt` to get
 /// readable source code.
-pub fn generate(sources: &ast::SourceDatabase, file: &analyzer_ast::File) -> String {
+pub fn generate_tokens(
+    sources: &ast::SourceDatabase,
+    file: &analyzer_ast::File,
+) -> proc_macro2::TokenStream {
     let source = sources.get(file.file).expect("could not read source");
     let preamble = preamble::generate(Path::new(source.name()));
 
     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! {
+    quote! {
         #preamble
 
         #(#decls)*
-    };
-    let syntax_tree = syn::parse2(code).expect("Could not parse code");
+    }
+}
+
+/// Generate formatted Rust code from an AST.
+///
+/// The code is not formatted, pipe it through `rustfmt` to get
+/// readable source code.
+pub fn generate(sources: &ast::SourceDatabase, file: &analyzer_ast::File) -> String {
+    let syntax_tree = syn::parse2(generate_tokens(sources, file)).expect("Could not parse code");
     prettyplease::unparse(&syntax_tree)
 }
 
@@ -1017,8 +1063,7 @@
     /// Panics on parse errors.
     pub fn parse_str(text: &str) -> analyzer_ast::File {
         let mut db = ast::SourceDatabase::new();
-        let file =
-            parse_inline(&mut db, String::from("stdin"), String::from(text)).expect("parse error");
+        let file = parse_inline(&mut db, "stdin", String::from(text)).expect("parse error");
         analyzer::analyze(&file).expect("analyzer error")
     }
 
@@ -1083,7 +1128,7 @@
                     let endianness = stringify!($endianness);
                     let code = format!("{endianness}_packets\n{}", $code);
                     let mut db = ast::SourceDatabase::new();
-                    let file = parse_inline(&mut db, String::from("test"), code).unwrap();
+                    let file = parse_inline(&mut db, "test", code).unwrap();
                     let file = analyzer::analyze(&file).unwrap();
                     let actual_code = generate(&db, &file);
                     assert_snapshot_eq(
diff --git a/src/backends/rust/parser.rs b/src/backends/rust/parser.rs
index 01c1720..8eaee06 100644
--- a/src/backends/rust/parser.rs
+++ b/src/backends/rust/parser.rs
@@ -65,6 +65,7 @@
 
     pub fn add(&mut self, field: &'a analyzer_ast::Field) {
         match &field.desc {
+            _ if field.cond.is_some() => self.add_optional_field(field),
             _ if self.scope.is_bitfield(field) => self.add_bit_field(field),
             ast::FieldDesc::Padding { .. } => (),
             ast::FieldDesc::Array { id, width, type_id, size, .. } => self.add_array_field(
@@ -84,6 +85,60 @@
         }
     }
 
+    fn add_optional_field(&mut self, field: &'a analyzer_ast::Field) {
+        let cond_id = field.cond.as_ref().unwrap().id.to_ident();
+        let cond_value = syn::parse_str::<syn::LitInt>(&format!(
+            "{}",
+            field.cond.as_ref().unwrap().value.unwrap()
+        ))
+        .unwrap();
+
+        self.code.push(
+            match &field.desc {
+                ast::FieldDesc::Scalar { id, width } => {
+                    let id = id.to_ident();
+                    let value = types::get_uint(self.endianness, *width, self.span);
+                    quote! {
+                        let #id = (#cond_id == #cond_value).then(|| #value);
+                    }
+                },
+                ast::FieldDesc::Typedef { id, type_id } =>
+                    match &self.scope.typedef[type_id].desc {
+                        ast::DeclDesc::Enum { width, .. } => {
+                            let name = id;
+                            let type_name = type_id;
+                            let id = id.to_ident();
+                            let type_id = type_id.to_ident();
+                            let decl_id = &self.packet_name;
+                            let value = types::get_uint(self.endianness, *width, self.span);
+                            quote! {
+                                let #id = (#cond_id == #cond_value)
+                                    .then(||
+                                        #type_id::try_from(#value).map_err(|unknown_val| Error::InvalidEnumValueError {
+                                            obj: #decl_id.to_string(),
+                                            field: #name.to_string(),
+                                            value: unknown_val as u64,
+                                            type_: #type_name.to_string(),
+                                        }))
+                                    .transpose()?;
+                            }
+                        }
+                        ast::DeclDesc::Struct { .. } => {
+                            let id = id.to_ident();
+                            let type_id = type_id.to_ident();
+                            let span = self.span;
+                            quote! {
+                                let #id = (#cond_id == #cond_value)
+                                    .then(|| #type_id::parse_inner(&mut #span))
+                                    .transpose()?;
+                            }
+                        }
+                        _ => unreachable!(),
+                    }
+                _ => unreachable!(),
+            })
+    }
+
     fn add_bit_field(&mut self, field: &'a analyzer_ast::Field) {
         self.chunk.push(BitField { shift: self.shift, field });
         self.shift += field.annot.size.static_().unwrap();
@@ -141,7 +196,8 @@
             }
 
             self.code.push(match &field.desc {
-                ast::FieldDesc::Scalar { id, .. } => {
+                ast::FieldDesc::Scalar { id, .. }
+                | ast::FieldDesc::Flag { id, .. } => {
                     let id = id.to_ident();
                     quote! {
                         let #id = #v;
@@ -741,8 +797,7 @@
     /// Panics on parse errors.
     pub fn parse_str(text: &str) -> analyzer_ast::File {
         let mut db = ast::SourceDatabase::new();
-        let file =
-            parse_inline(&mut db, String::from("stdin"), String::from(text)).expect("parse error");
+        let file = parse_inline(&mut db, "stdin", String::from(text)).expect("parse error");
         analyzer::analyze(&file).expect("analyzer error")
     }
 
diff --git a/src/backends/rust/preamble.rs b/src/backends/rust/preamble.rs
index ce8b742..e9af398 100644
--- a/src/backends/rust/preamble.rs
+++ b/src/backends/rust/preamble.rs
@@ -55,7 +55,7 @@
         use std::convert::{TryFrom, TryInto};
         use std::cell::Cell;
         use std::fmt;
-        use thiserror::Error;
+        use pdl_runtime::{Error, Packet};
 
         type Result<T> = std::result::Result<T, Error>;
 
@@ -72,31 +72,6 @@
                 &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>;
-        }
     }
 }
 
diff --git a/src/backends/rust/serializer.rs b/src/backends/rust/serializer.rs
index 345e98a..64bc567 100644
--- a/src/backends/rust/serializer.rs
+++ b/src/backends/rust/serializer.rs
@@ -54,6 +54,7 @@
 
     pub fn add(&mut self, field: &analyzer_ast::Field) {
         match &field.desc {
+            _ if field.cond.is_some() => self.add_optional_field(field),
             _ if self.scope.is_bitfield(field) => self.add_bit_field(field),
             ast::FieldDesc::Array { id, width, .. } => self.add_array_field(
                 id,
@@ -73,11 +74,89 @@
         }
     }
 
+    fn add_optional_field(&mut self, field: &analyzer_ast::Field) {
+        self.code.push(match &field.desc {
+            ast::FieldDesc::Scalar { id, width } => {
+                let name = id;
+                let id = id.to_ident();
+                let backing_type = types::Integer::new(*width);
+                let write = types::put_uint(self.endianness, &quote!(*#id), *width, self.span);
+
+                let range_check = (backing_type.width > *width).then(|| {
+                    let packet_name = &self.packet_name;
+                    let max_value = mask_bits(*width, "u64");
+
+                    quote! {
+                        if *#id > #max_value {
+                            panic!(
+                                "Invalid value for {}::{}: {} > {}",
+                                #packet_name, #name, #id, #max_value
+                            );
+                        }
+                    }
+                });
+
+                quote! {
+                    if let Some(#id) = &self.#id {
+                        #range_check
+                        #write
+                    }
+                }
+            }
+            ast::FieldDesc::Typedef { id, type_id } => match &self.scope.typedef[type_id].desc {
+                ast::DeclDesc::Enum { width, .. } => {
+                    let id = id.to_ident();
+                    let backing_type = types::Integer::new(*width);
+                    let write = types::put_uint(
+                        self.endianness,
+                        &quote!(#backing_type::from(#id)),
+                        *width,
+                        self.span,
+                    );
+                    quote! {
+                        if let Some(#id) = &self.#id {
+                            #write
+                        }
+                    }
+                }
+                ast::DeclDesc::Struct { .. } => {
+                    let id = id.to_ident();
+                    let span = self.span;
+                    quote! {
+                        if let Some(#id) = &self.#id {
+                            #id.write_to(#span);
+                        }
+                    }
+                }
+                _ => unreachable!(),
+            },
+            _ => unreachable!(),
+        })
+    }
+
     fn add_bit_field(&mut self, field: &analyzer_ast::Field) {
         let width = field.annot.size.static_().unwrap();
         let shift = self.shift;
 
         match &field.desc {
+            ast::FieldDesc::Flag { optional_field_id, set_value, .. } => {
+                let optional_field_id = optional_field_id.to_ident();
+                let cond_value_present =
+                    syn::parse_str::<syn::LitInt>(&format!("{}", set_value)).unwrap();
+                let cond_value_absent =
+                    syn::parse_str::<syn::LitInt>(&format!("{}", 1 - set_value)).unwrap();
+                self.chunk.push(BitField {
+                    value: quote! {
+                        if self.#optional_field_id.is_some() {
+                            #cond_value_present
+                        } else {
+                            #cond_value_absent
+                        }
+                    },
+                    field_type: types::Integer::new(1),
+                    shift,
+                });
+            }
             ast::FieldDesc::Scalar { id, width } => {
                 let field_name = id.to_ident();
                 let field_type = types::Integer::new(*width);
diff --git a/src/backends/rust/types.rs b/src/backends/rust/types.rs
index b5f2b91..799cd4e 100644
--- a/src/backends/rust/types.rs
+++ b/src/backends/rust/types.rs
@@ -51,10 +51,18 @@
 
 pub fn rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream {
     match &field.desc {
+        ast::FieldDesc::Scalar { width, .. } if field.cond.is_some() => {
+            let field_type = Integer::new(*width);
+            quote!(Option<#field_type>)
+        }
         ast::FieldDesc::Scalar { width, .. } => {
             let field_type = Integer::new(*width);
             quote!(#field_type)
         }
+        ast::FieldDesc::Typedef { type_id, .. } if field.cond.is_some() => {
+            let field_type = type_id.to_ident();
+            quote!(Option<#field_type>)
+        }
         ast::FieldDesc::Typedef { type_id, .. } => {
             let field_type = type_id.to_ident();
             quote!(#field_type)
diff --git a/src/backends/rust_no_allocation/packet_parser.rs b/src/backends/rust_no_allocation/packet_parser.rs
index 44342fb..bd07630 100644
--- a/src/backends/rust_no_allocation/packet_parser.rs
+++ b/src/backends/rust_no_allocation/packet_parser.rs
@@ -55,6 +55,7 @@
     let field_getters = fields.iter().map(|field| {
         match &field.desc {
             ast::FieldDesc::Padding { .. }
+            | ast::FieldDesc::Flag { .. }
             | ast::FieldDesc::Reserved { .. }
             | ast::FieldDesc::FixedScalar { .. }
             | ast::FieldDesc::FixedEnum { .. }
@@ -233,6 +234,7 @@
         ast::FieldDesc::Checksum { .. } => unimplemented!(),
         ast::FieldDesc::Group { .. } => unreachable!(),
         ast::FieldDesc::Padding { .. }
+        | ast::FieldDesc::Flag { .. }
         | ast::FieldDesc::Size { .. }
         | ast::FieldDesc::Count { .. }
         | ast::FieldDesc::ElementSize { .. }
diff --git a/src/backends/rust_no_allocation/packet_serializer.rs b/src/backends/rust_no_allocation/packet_serializer.rs
index 9ecae38..c088b75 100644
--- a/src/backends/rust_no_allocation/packet_serializer.rs
+++ b/src/backends/rust_no_allocation/packet_serializer.rs
@@ -48,6 +48,7 @@
         .filter_map(|field| {
             match &field.desc {
                 ast::FieldDesc::Padding { .. }
+                | ast::FieldDesc::Flag { .. }
                 | ast::FieldDesc::Reserved { .. }
                 | ast::FieldDesc::FixedScalar { .. }
                 | ast::FieldDesc::FixedEnum { .. }
@@ -102,7 +103,7 @@
 
     let serializer = fields.iter().map(|field| {
         match &field.desc {
-            ast::FieldDesc::Checksum { .. } | ast::FieldDesc::Group { .. } => unimplemented!(),
+            ast::FieldDesc::Checksum { .. } | ast::FieldDesc::Group { .. } | ast::FieldDesc::Flag { .. } => unimplemented!(),
             ast::FieldDesc::Padding { size, .. } => {
                 quote! {
                     if (most_recent_array_size_in_bits > #size * 8) {
diff --git a/src/backends/rust_no_allocation/test.rs b/src/backends/rust_no_allocation/test.rs
index 18aa82b..a505b1d 100644
--- a/src/backends/rust_no_allocation/test.rs
+++ b/src/backends/rust_no_allocation/test.rs
@@ -215,7 +215,7 @@
         serde_json::from_str(file).map_err(|_| "could not parse test vectors")?;
 
     let pdl = include_str!("../../../tests/canonical/le_rust_noalloc_test_file.pdl");
-    let ast = parse_inline(&mut ast::SourceDatabase::new(), "test.pdl".to_owned(), pdl.to_owned())
+    let ast = parse_inline(&mut ast::SourceDatabase::new(), "test.pdl", pdl.to_owned())
         .expect("could not parse reference PDL");
     let packet_lookup =
         ast.declarations
diff --git a/src/bin/generate-canonical-tests.rs b/src/bin/generate-canonical-tests.rs
index 44ad2b1..d044edf 100644
--- a/src/bin/generate-canonical-tests.rs
+++ b/src/bin/generate-canonical-tests.rs
@@ -65,7 +65,7 @@
         .unwrap_or_else(|err| panic!("Could not read {input}: {err}"));
     let packets: Vec<Packet> = serde_json::from_str(&data).expect("Could not parse JSON");
 
-    let module = format_ident!("{}", module_name);
+    let module = syn::parse_str::<syn::Path>(module_name).unwrap();
     let mut tests = Vec::new();
     for packet in &packets {
         for (i, test_vector) in packet.tests.iter().enumerate() {
@@ -131,7 +131,7 @@
     let code = quote! {
         #![allow(warnings, missing_docs)]
 
-        use #module::Packet;
+        use pdl_runtime::Packet;
         use serde_json::json;
 
         #(#tests)*
@@ -174,6 +174,9 @@
             "Packet_Array_Field_UnsizedElement_VariableSize",
             "Packet_Array_Field_SizedElement_VariableSize_Padded",
             "Packet_Array_Field_UnsizedElement_VariableCount_Padded",
+            "Packet_Optional_Scalar_Field",
+            "Packet_Optional_Enum_Field",
+            "Packet_Optional_Struct_Field",
             "Packet_Body_Field_UnknownSize",
             "Packet_Body_Field_UnknownSize_Terminal",
             "Packet_Body_Field_VariableSize",
@@ -229,6 +232,9 @@
             "Struct_Array_Field_UnsizedElement_VariableSize",
             "Struct_Array_Field_SizedElement_VariableSize_Padded",
             "Struct_Array_Field_UnsizedElement_VariableCount_Padded",
+            "Struct_Optional_Scalar_Field",
+            "Struct_Optional_Enum_Field",
+            "Struct_Optional_Struct_Field",
             "Struct_Enum_Field",
             "Struct_FixedEnum_Field",
             "Struct_FixedScalar_Field",
diff --git a/src/main.rs b/src/main.rs
index f550117..8992d29 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -89,7 +89,7 @@
     }
 
     let mut sources = ast::SourceDatabase::new();
-    match parser::parse_file(&mut sources, opt.input_file) {
+    match parser::parse_file(&mut sources, &opt.input_file) {
         Ok(file) => {
             let file = filter_declarations(file, &opt.exclude_declaration);
             let analyzed_file = match analyzer::analyze(&file) {
diff --git a/src/parser.rs b/src/parser.rs
index 1104f68..29407ff 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -35,12 +35,18 @@
 }
 
 // Generate the PDL parser.
-// TODO: use #[grammar = "pdl.pest"]
-// currently not possible because CARGO_MANIFEST_DIR is not set
-// in soong environment.
+//
+// TODO:
+// - use #[grammar = "pdl.pest"]
+//   currently not possible because CARGO_MANIFEST_DIR is not set
+//   in soong environment.
+// - use silent atomic rules for keywords like
+//   ENUM = @{ "enum" ~ WHITESPACE }
+//   currently not implemented in pest:
+//   https://github.com/pest-parser/pest/issues/520
 #[derive(pest_derive::Parser)]
 #[grammar_inline = r#"
-WHITESPACE = _{ " " | "\n" }
+WHITESPACE = _{ " " | "\n" | "\r" | "\t" }
 COMMENT = { block_comment | line_comment }
 
 block_comment = { "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
@@ -60,7 +66,15 @@
 string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
 size_modifier = @{ "+" ~ intvalue }
 
-endianness_declaration = { "little_endian_packets" | "big_endian_packets" }
+ENUM = @{ "enum" ~ WHITESPACE }
+PACKET = @{ "packet" ~ WHITESPACE }
+STRUCT = @{ "struct" ~ WHITESPACE }
+GROUP = @{ "group" ~ WHITESPACE }
+CHECKSUM = @{ "checksum" ~ WHITESPACE }
+CUSTOM_FIELD = @{ "custom_field" ~ WHITESPACE }
+TEST = @{ "test" ~ WHITESPACE }
+
+endianness_declaration = ${ ("little_endian_packets" | "big_endian_packets") ~ WHITESPACE }
 
 enum_value = { identifier ~ "=" ~ integer }
 enum_value_list = { enum_value ~ ("," ~ enum_value)* ~ ","? }
@@ -73,7 +87,7 @@
 enum_tag = { enum_range | enum_value | enum_other }
 enum_tag_list = { enum_tag ~ ("," ~ enum_tag)* ~ ","? }
 enum_declaration = {
-    "enum" ~ identifier ~ ":" ~ integer ~ "{" ~
+    ENUM ~ identifier ~ ":" ~ integer ~ "{" ~
         enum_tag_list ~
     "}"
 }
@@ -100,7 +114,7 @@
 typedef_field = { identifier ~ ":" ~ identifier }
 group_field = { identifier ~ ("{" ~ constraint_list? ~ "}")? }
 
-field = _{
+field_desc = _{
     checksum_field |
     padding_field |
     size_field |
@@ -115,10 +129,11 @@
     typedef_field |
     group_field
 }
+field = { field_desc ~ ("if" ~ constraint)? }
 field_list = { field ~ ("," ~ field)* ~ ","? }
 
 packet_declaration = {
-   "packet" ~ identifier ~
+   PACKET ~ identifier ~
         (":" ~ identifier)? ~
            ("(" ~ constraint_list ~ ")")? ~
     "{" ~
@@ -127,7 +142,7 @@
 }
 
 struct_declaration = {
-    "struct" ~ identifier ~
+    STRUCT ~ identifier ~
         (":" ~ identifier)? ~
            ("(" ~ constraint_list ~ ")")? ~
     "{" ~
@@ -136,21 +151,21 @@
 }
 
 group_declaration = {
-    "group" ~ identifier ~ "{" ~ field_list ~ "}"
+    GROUP ~ identifier ~ "{" ~ field_list ~ "}"
 }
 
 checksum_declaration = {
-    "checksum" ~ identifier ~ ":" ~ integer ~ string
+    CHECKSUM ~ identifier ~ ":" ~ integer ~ string
 }
 
 custom_field_declaration = {
-    "custom_field" ~ identifier ~ (":" ~ integer)? ~ string
+    CUSTOM_FIELD ~ identifier ~ (":" ~ integer)? ~ string
 }
 
 test_case = { string }
 test_case_list = _{ test_case ~ ("," ~ test_case)* ~ ","? }
 test_declaration = {
-    "test" ~ identifier ~ "{" ~
+    TEST ~ identifier ~ "{" ~
         test_case_list ~
     "}"
 }
@@ -288,7 +303,7 @@
     } else {
         Ok(crate::ast::Endianness {
             loc: node.as_loc(context),
-            value: match node.as_str() {
+            value: match node.as_str().trim() {
                 "little_endian_packets" => crate::ast::EndiannessValue::LittleEndian,
                 "big_endian_packets" => crate::ast::EndiannessValue::BigEndian,
                 _ => unreachable!(),
@@ -401,11 +416,15 @@
 
 fn parse_field(node: Node<'_>, context: &Context) -> Result<ast::Field, String> {
     let loc = node.as_loc(context);
-    let rule = node.as_rule();
     let mut children = node.children();
+    let desc = children.next().unwrap();
+    let cond = children.next();
+    let rule = desc.as_rule();
+    let mut children = desc.children();
     Ok(crate::ast::Field {
         loc,
         annot: Default::default(),
+        cond: cond.map(|constraint| parse_constraint(constraint, context)).transpose()?,
         desc: match rule {
             Rule::checksum_field => {
                 let field_id = parse_identifier(&mut children)?;
@@ -539,6 +558,7 @@
             Rule::endianness_declaration => file.endianness = parse_endianness(node, context)?,
             Rule::checksum_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::CHECKSUM)?;
                 let id = parse_identifier(&mut children)?;
                 let width = parse_integer(&mut children)?;
                 let function = parse_string(&mut children)?;
@@ -549,6 +569,7 @@
             }
             Rule::custom_field_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::CUSTOM_FIELD)?;
                 let id = parse_identifier(&mut children)?;
                 let width = parse_integer_opt(&mut children)?;
                 let function = parse_string(&mut children)?;
@@ -559,6 +580,7 @@
             }
             Rule::enum_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::ENUM)?;
                 let id = parse_identifier(&mut children)?;
                 let width = parse_integer(&mut children)?;
                 let tags = parse_enum_tag_list(&mut children, context)?;
@@ -569,6 +591,7 @@
             }
             Rule::packet_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::PACKET)?;
                 let id = parse_identifier(&mut children)?;
                 let parent_id = parse_identifier_opt(&mut children)?;
                 let constraints = parse_constraint_list_opt(&mut children, context)?;
@@ -580,6 +603,7 @@
             }
             Rule::struct_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::STRUCT)?;
                 let id = parse_identifier(&mut children)?;
                 let parent_id = parse_identifier_opt(&mut children)?;
                 let constraints = parse_constraint_list_opt(&mut children, context)?;
@@ -591,6 +615,7 @@
             }
             Rule::group_declaration => {
                 let mut children = node.children();
+                expect(&mut children, Rule::GROUP)?;
                 let id = parse_identifier(&mut children)?;
                 let fields = parse_field_list(&mut children, context)?;
                 file.declarations
@@ -611,18 +636,18 @@
 /// name.
 pub fn parse_inline(
     sources: &mut crate::ast::SourceDatabase,
-    name: String,
+    name: &str,
     source: String,
 ) -> Result<ast::File, Diagnostic<crate::ast::FileId>> {
     let root = PDLParser::parse(Rule::file, &source)
         .map_err(|e| {
             Diagnostic::error()
-                .with_message(format!("failed to parse input file '{}': {}", &name, e))
+                .with_message(format!("failed to parse input file '{}': {}", name, e))
         })?
         .next()
         .unwrap();
     let line_starts: Vec<_> = files::line_starts(&source).collect();
-    let file = sources.add(name, source.clone());
+    let file = sources.add(name.to_owned(), source.clone());
     parse_toplevel(root, &(file, &line_starts)).map_err(|e| Diagnostic::error().with_message(e))
 }
 
@@ -633,10 +658,10 @@
 /// message in case of syntax error.
 pub fn parse_file(
     sources: &mut crate::ast::SourceDatabase,
-    name: String,
+    name: &str,
 ) -> Result<ast::File, Diagnostic<crate::ast::FileId>> {
-    let source = std::fs::read_to_string(&name).map_err(|e| {
-        Diagnostic::error().with_message(format!("failed to read input file '{}': {}", &name, e))
+    let source = std::fs::read_to_string(name).map_err(|e| {
+        Diagnostic::error().with_message(format!("failed to read input file '{}': {}", name, e))
     })?;
     parse_inline(sources, name, source)
 }
@@ -650,9 +675,7 @@
         // The file starts out with a placeholder little-endian value.
         // This tests that we update it while parsing.
         let mut db = crate::ast::SourceDatabase::new();
-        let file =
-            parse_inline(&mut db, String::from("stdin"), String::from("  big_endian_packets  "))
-                .unwrap();
+        let file = parse_inline(&mut db, "stdin", String::from("  big_endian_packets  ")).unwrap();
         assert_eq!(file.endianness.value, crate::ast::EndiannessValue::BigEndian);
         assert_ne!(file.endianness.loc, crate::ast::SourceRange::default());
     }
@@ -681,4 +704,32 @@
         assert_eq!(parse_string(&mut pairs).as_deref(), Ok(r#""test""#));
         assert_eq!(pairs.next(), None, "pairs is empty");
     }
+
+    #[test]
+    fn test_no_whitespace_between_keywords() {
+        // Validate that the parser rejects inputs where whitespaces
+        // are not applied between alphabetical keywords and identifiers.
+        let mut db = crate::ast::SourceDatabase::new();
+        assert!(parse_inline(
+            &mut db,
+            "test",
+            r#"
+            little_endian_packetsstructx{foo:8}
+            "#
+            .to_owned()
+        )
+        .is_err());
+
+        let result = parse_inline(
+            &mut db,
+            "test",
+            r#"
+            little_endian_packets
+            struct x { foo:8 }
+            "#
+            .to_owned(),
+        );
+        println!("{:?}", result);
+        assert!(result.is_ok());
+    }
 }
diff --git a/tests/canonical/be_test_vectors.json b/tests/canonical/be_test_vectors.json
index e03357e..b6e1b91 100644
--- a/tests/canonical/be_test_vectors.json
+++ b/tests/canonical/be_test_vectors.json
@@ -2155,6 +2155,330 @@
     ]
   },
   {
+    "packet": "Packet_Optional_Scalar_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "0300000000",
+        "unpacked": {
+          "a": null,
+          "b": 0
+        }
+      },
+      {
+        "packed": "03ffffffff",
+        "unpacked": {
+          "a": null,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "0384141c25",
+        "unpacked": {
+          "a": null,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "00000000",
+        "unpacked": {
+          "a": 0,
+          "b": null
+        }
+      },
+      {
+        "packed": "0200000000000000",
+        "unpacked": {
+          "a": 0,
+          "b": 0
+        }
+      },
+      {
+        "packed": "02000000ffffffff",
+        "unpacked": {
+          "a": 0,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "0200000084141c25",
+        "unpacked": {
+          "a": 0,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "00ffffff",
+        "unpacked": {
+          "a": 16777215,
+          "b": null
+        }
+      },
+      {
+        "packed": "02ffffff00000000",
+        "unpacked": {
+          "a": 16777215,
+          "b": 0
+        }
+      },
+      {
+        "packed": "02ffffffffffffff",
+        "unpacked": {
+          "a": 16777215,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "02ffffff84141c25",
+        "unpacked": {
+          "a": 16777215,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "007bfc01",
+        "unpacked": {
+          "a": 8125441,
+          "b": null
+        }
+      },
+      {
+        "packed": "027bfc0100000000",
+        "unpacked": {
+          "a": 8125441,
+          "b": 0
+        }
+      },
+      {
+        "packed": "027bfc01ffffffff",
+        "unpacked": {
+          "a": 8125441,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "027bfc0184141c25",
+        "unpacked": {
+          "a": 8125441,
+          "b": 2215910437
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Packet_Optional_Enum_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "03aabb",
+        "unpacked": {
+          "a": null,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "03ccdd",
+        "unpacked": {
+          "a": null,
+          "b": 52445
+        }
+      },
+      {
+        "packed": "00aabb",
+        "unpacked": {
+          "a": 43707,
+          "b": null
+        }
+      },
+      {
+        "packed": "02aabbaabb",
+        "unpacked": {
+          "a": 43707,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "02aabbccdd",
+        "unpacked": {
+          "a": 43707,
+          "b": 52445
+        }
+      },
+      {
+        "packed": "00ccdd",
+        "unpacked": {
+          "a": 52445,
+          "b": null
+        }
+      },
+      {
+        "packed": "02ccddaabb",
+        "unpacked": {
+          "a": 52445,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "02ccddccdd",
+        "unpacked": {
+          "a": 52445,
+          "b": 52445
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Packet_Optional_Struct_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "0300",
+        "unpacked": {
+          "a": null,
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "0303878089",
+        "unpacked": {
+          "a": null,
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "0000",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "020000",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "020003878089",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "00ff",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "02ff00",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "02ff03878089",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "0086",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "028600",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "028603878089",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      }
+    ]
+  },
+  {
     "packet": "ScalarParent",
     "tests": [
       {
@@ -4267,5 +4591,403 @@
         }
       }
     ]
+  },
+  {
+    "packet": "Struct_Optional_Scalar_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0300000000",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "03ffffffff",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "03f777b7e0",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 4151818208
+          }
+        }
+      },
+      {
+        "packed": "00000000",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0200000000000000",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02000000ffffffff",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02000000f777b7e0",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 4151818208
+          }
+        }
+      },
+      {
+        "packed": "00ffffff",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ffffff00000000",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02ffffffffffffff",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02fffffff777b7e0",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 4151818208
+          }
+        }
+      },
+      {
+        "packed": "00f6b6dc",
+        "unpacked": {
+          "s": {
+            "a": 16168668,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02f6b6dc00000000",
+        "unpacked": {
+          "s": {
+            "a": 16168668,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02f6b6dcffffffff",
+        "unpacked": {
+          "s": {
+            "a": 16168668,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02f6b6dcf777b7e0",
+        "unpacked": {
+          "s": {
+            "a": 16168668,
+            "b": 4151818208
+          }
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Struct_Optional_Enum_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "03aabb",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "03ccdd",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 52445
+          }
+        }
+      },
+      {
+        "packed": "00aabb",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02aabbaabb",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "02aabbccdd",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": 52445
+          }
+        }
+      },
+      {
+        "packed": "00ccdd",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ccddaabb",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "02ccddccdd",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": 52445
+          }
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Struct_Optional_Struct_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0300",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "0303e2e3e4",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": {
+              "array": [
+                226,
+                227,
+                228
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "0000",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "020000",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "020003e2e3e4",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": {
+              "array": [
+                226,
+                227,
+                228
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "00ff",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ff00",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "02ff03e2e3e4",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": {
+              "array": [
+                226,
+                227,
+                228
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "00e1",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 225
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02e100",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 225
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "02e103e2e3e4",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 225
+            },
+            "b": {
+              "array": [
+                226,
+                227,
+                228
+              ]
+            }
+          }
+        }
+      }
+    ]
   }
-]
\ No newline at end of file
+]
diff --git a/tests/canonical/le_test_file.pdl b/tests/canonical/le_test_file.pdl
index 6bc140c..fa87314 100644
--- a/tests/canonical/le_test_file.pdl
+++ b/tests/canonical/le_test_file.pdl
@@ -362,6 +362,30 @@
     _padding_ [16],
 }
 
+packet Packet_Optional_Scalar_Field {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: 24 if c0 = 0,
+    b: 32 if c1 = 1,
+}
+
+packet Packet_Optional_Enum_Field {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: Enum16 if c0 = 0,
+    b: Enum16 if c1 = 1,
+}
+
+packet Packet_Optional_Struct_Field {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: SizedStruct if c0 = 0,
+    b: UnsizedStruct if c1 = 1,
+}
+
 // Packet inheritance
 
 // The parser must handle specialization into
@@ -778,3 +802,39 @@
 packet Struct_Array_Field_UnsizedElement_VariableCount_Padded {
     s: Struct_Array_Field_UnsizedElement_VariableCount_Padded_,
 }
+
+struct Struct_Optional_Scalar_Field_ {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: 24 if c0 = 0,
+    b: 32 if c1 = 1,
+}
+
+packet Struct_Optional_Scalar_Field {
+    s: Struct_Optional_Scalar_Field_,
+}
+
+struct Struct_Optional_Enum_Field_ {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: Enum16 if c0 = 0,
+    b: Enum16 if c1 = 1,
+}
+
+packet Struct_Optional_Enum_Field {
+    s: Struct_Optional_Enum_Field_,
+}
+
+struct Struct_Optional_Struct_Field_ {
+    c0: 1,
+    c1: 1,
+    _reserved_: 6,
+    a: SizedStruct if c0 = 0,
+    b: UnsizedStruct if c1 = 1,
+}
+
+packet Struct_Optional_Struct_Field {
+    s: Struct_Optional_Struct_Field_,
+}
diff --git a/tests/canonical/le_test_vectors.json b/tests/canonical/le_test_vectors.json
index 243952c..9ddff45 100644
--- a/tests/canonical/le_test_vectors.json
+++ b/tests/canonical/le_test_vectors.json
@@ -2155,6 +2155,330 @@
     ]
   },
   {
+    "packet": "Packet_Optional_Scalar_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "0300000000",
+        "unpacked": {
+          "a": null,
+          "b": 0
+        }
+      },
+      {
+        "packed": "03ffffffff",
+        "unpacked": {
+          "a": null,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "03251c1484",
+        "unpacked": {
+          "a": null,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "00000000",
+        "unpacked": {
+          "a": 0,
+          "b": null
+        }
+      },
+      {
+        "packed": "0200000000000000",
+        "unpacked": {
+          "a": 0,
+          "b": 0
+        }
+      },
+      {
+        "packed": "02000000ffffffff",
+        "unpacked": {
+          "a": 0,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "02000000251c1484",
+        "unpacked": {
+          "a": 0,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "00ffffff",
+        "unpacked": {
+          "a": 16777215,
+          "b": null
+        }
+      },
+      {
+        "packed": "02ffffff00000000",
+        "unpacked": {
+          "a": 16777215,
+          "b": 0
+        }
+      },
+      {
+        "packed": "02ffffffffffffff",
+        "unpacked": {
+          "a": 16777215,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "02ffffff251c1484",
+        "unpacked": {
+          "a": 16777215,
+          "b": 2215910437
+        }
+      },
+      {
+        "packed": "0001fc7b",
+        "unpacked": {
+          "a": 8125441,
+          "b": null
+        }
+      },
+      {
+        "packed": "0201fc7b00000000",
+        "unpacked": {
+          "a": 8125441,
+          "b": 0
+        }
+      },
+      {
+        "packed": "0201fc7bffffffff",
+        "unpacked": {
+          "a": 8125441,
+          "b": 4294967295
+        }
+      },
+      {
+        "packed": "0201fc7b251c1484",
+        "unpacked": {
+          "a": 8125441,
+          "b": 2215910437
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Packet_Optional_Enum_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "03bbaa",
+        "unpacked": {
+          "a": null,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "03ddcc",
+        "unpacked": {
+          "a": null,
+          "b": 52445
+        }
+      },
+      {
+        "packed": "00bbaa",
+        "unpacked": {
+          "a": 43707,
+          "b": null
+        }
+      },
+      {
+        "packed": "02bbaabbaa",
+        "unpacked": {
+          "a": 43707,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "02bbaaddcc",
+        "unpacked": {
+          "a": 43707,
+          "b": 52445
+        }
+      },
+      {
+        "packed": "00ddcc",
+        "unpacked": {
+          "a": 52445,
+          "b": null
+        }
+      },
+      {
+        "packed": "02ddccbbaa",
+        "unpacked": {
+          "a": 52445,
+          "b": 43707
+        }
+      },
+      {
+        "packed": "02ddccddcc",
+        "unpacked": {
+          "a": 52445,
+          "b": 52445
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Packet_Optional_Struct_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "a": null,
+          "b": null
+        }
+      },
+      {
+        "packed": "0300",
+        "unpacked": {
+          "a": null,
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "0303878089",
+        "unpacked": {
+          "a": null,
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "0000",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "020000",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "020003878089",
+        "unpacked": {
+          "a": {
+            "a": 0
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "00ff",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "02ff00",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "02ff03878089",
+        "unpacked": {
+          "a": {
+            "a": 255
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      },
+      {
+        "packed": "0086",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": null
+        }
+      },
+      {
+        "packed": "028600",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": {
+            "array": []
+          }
+        }
+      },
+      {
+        "packed": "028603878089",
+        "unpacked": {
+          "a": {
+            "a": 134
+          },
+          "b": {
+            "array": [
+              135,
+              128,
+              137
+            ]
+          }
+        }
+      }
+    ]
+  },
+  {
     "packet": "ScalarParent",
     "tests": [
       {
@@ -4373,5 +4697,403 @@
         }
       }
     ]
+  },
+  {
+    "packet": "Struct_Optional_Scalar_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0300000000",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "03ffffffff",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "03786e5eee",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 3999166072
+          }
+        }
+      },
+      {
+        "packed": "00000000",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0200000000000000",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02000000ffffffff",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02000000786e5eee",
+        "unpacked": {
+          "s": {
+            "a": 0,
+            "b": 3999166072
+          }
+        }
+      },
+      {
+        "packed": "00ffffff",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ffffff00000000",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02ffffffffffffff",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02ffffff786e5eee",
+        "unpacked": {
+          "s": {
+            "a": 16777215,
+            "b": 3999166072
+          }
+        }
+      },
+      {
+        "packed": "00342eee",
+        "unpacked": {
+          "s": {
+            "a": 15609396,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02342eee00000000",
+        "unpacked": {
+          "s": {
+            "a": 15609396,
+            "b": 0
+          }
+        }
+      },
+      {
+        "packed": "02342eeeffffffff",
+        "unpacked": {
+          "s": {
+            "a": 15609396,
+            "b": 4294967295
+          }
+        }
+      },
+      {
+        "packed": "02342eee786e5eee",
+        "unpacked": {
+          "s": {
+            "a": 15609396,
+            "b": 3999166072
+          }
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Struct_Optional_Enum_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "03bbaa",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "03ddcc",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": 52445
+          }
+        }
+      },
+      {
+        "packed": "00bbaa",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02bbaabbaa",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "02bbaaddcc",
+        "unpacked": {
+          "s": {
+            "a": 43707,
+            "b": 52445
+          }
+        }
+      },
+      {
+        "packed": "00ddcc",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ddccbbaa",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": 43707
+          }
+        }
+      },
+      {
+        "packed": "02ddccddcc",
+        "unpacked": {
+          "s": {
+            "a": 52445,
+            "b": 52445
+          }
+        }
+      }
+    ]
+  },
+  {
+    "packet": "Struct_Optional_Struct_Field",
+    "tests": [
+      {
+        "packed": "01",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "0300",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "0303eaebec",
+        "unpacked": {
+          "s": {
+            "a": null,
+            "b": {
+              "array": [
+                234,
+                235,
+                236
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "0000",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "020000",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "020003eaebec",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 0
+            },
+            "b": {
+              "array": [
+                234,
+                235,
+                236
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "00ff",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02ff00",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "02ff03eaebec",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 255
+            },
+            "b": {
+              "array": [
+                234,
+                235,
+                236
+              ]
+            }
+          }
+        }
+      },
+      {
+        "packed": "00e9",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 233
+            },
+            "b": null
+          }
+        }
+      },
+      {
+        "packed": "02e900",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 233
+            },
+            "b": {
+              "array": []
+            }
+          }
+        }
+      },
+      {
+        "packed": "02e903eaebec",
+        "unpacked": {
+          "s": {
+            "a": {
+              "a": 233
+            },
+            "b": {
+              "array": [
+                234,
+                235,
+                236
+              ]
+            }
+          }
+        }
+      }
+    ]
   }
-]
\ No newline at end of file
+]
diff --git a/tests/generated/custom_field_declaration_big_endian.rs b/tests/generated/custom_field_declaration_big_endian.rs
index 8046f5a..eb70e2f 100644
--- a/tests/generated/custom_field_declaration_big_endian.rs
+++ b/tests/generated/custom_field_declaration_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serde", serde(from = "u32", into = "u32"))]
diff --git a/tests/generated/custom_field_declaration_little_endian.rs b/tests/generated/custom_field_declaration_little_endian.rs
index 8046f5a..eb70e2f 100644
--- a/tests/generated/custom_field_declaration_little_endian.rs
+++ b/tests/generated/custom_field_declaration_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serde", serde(from = "u32", into = "u32"))]
diff --git a/tests/generated/enum_declaration_big_endian.rs b/tests/generated/enum_declaration_big_endian.rs
index ffc4725..bd90591 100644
--- a/tests/generated/enum_declaration_big_endian.rs
+++ b/tests/generated/enum_declaration_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/enum_declaration_little_endian.rs b/tests/generated/enum_declaration_little_endian.rs
index ffc4725..bd90591 100644
--- a/tests/generated/enum_declaration_little_endian.rs
+++ b/tests/generated/enum_declaration_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 7717d7a..d1bba97 100644
--- a/tests/generated/packet_decl_24bit_enum_array_big_endian.rs
+++ b/tests/generated/packet_decl_24bit_enum_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 2fb9a9c..9a48b4b 100644
--- a/tests/generated/packet_decl_24bit_enum_array_little_endian.rs
+++ b/tests/generated/packet_decl_24bit_enum_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_24bit_enum_big_endian.rs b/tests/generated/packet_decl_24bit_enum_big_endian.rs
index 272e52d..64b6a81 100644
--- a/tests/generated/packet_decl_24bit_enum_big_endian.rs
+++ b/tests/generated/packet_decl_24bit_enum_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_24bit_enum_little_endian.rs b/tests/generated/packet_decl_24bit_enum_little_endian.rs
index 278336c..cd916a7 100644
--- a/tests/generated/packet_decl_24bit_enum_little_endian.rs
+++ b/tests/generated/packet_decl_24bit_enum_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_24bit_scalar_array_big_endian.rs b/tests/generated/packet_decl_24bit_scalar_array_big_endian.rs
index 193c5e5..a0a810a 100644
--- a/tests/generated/packet_decl_24bit_scalar_array_big_endian.rs
+++ b/tests/generated/packet_decl_24bit_scalar_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_24bit_scalar_array_little_endian.rs b/tests/generated/packet_decl_24bit_scalar_array_little_endian.rs
index fafba2c..adbdff4 100644
--- a/tests/generated/packet_decl_24bit_scalar_array_little_endian.rs
+++ b/tests/generated/packet_decl_24bit_scalar_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_24bit_scalar_big_endian.rs b/tests/generated/packet_decl_24bit_scalar_big_endian.rs
index 15e76b0..2bbdde0 100644
--- a/tests/generated/packet_decl_24bit_scalar_big_endian.rs
+++ b/tests/generated/packet_decl_24bit_scalar_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_24bit_scalar_little_endian.rs b/tests/generated/packet_decl_24bit_scalar_little_endian.rs
index 77ede78..fce2122 100644
--- a/tests/generated/packet_decl_24bit_scalar_little_endian.rs
+++ b/tests/generated/packet_decl_24bit_scalar_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
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 caeba78..fce2ae1 100644
--- a/tests/generated/packet_decl_64bit_enum_array_big_endian.rs
+++ b/tests/generated/packet_decl_64bit_enum_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 2a698b8..8d7c2a1 100644
--- a/tests/generated/packet_decl_64bit_enum_array_little_endian.rs
+++ b/tests/generated/packet_decl_64bit_enum_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_64bit_enum_big_endian.rs b/tests/generated/packet_decl_64bit_enum_big_endian.rs
index f29c654..2a0c784 100644
--- a/tests/generated/packet_decl_64bit_enum_big_endian.rs
+++ b/tests/generated/packet_decl_64bit_enum_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_64bit_enum_little_endian.rs b/tests/generated/packet_decl_64bit_enum_little_endian.rs
index 0bc5a12..0fcaa48 100644
--- a/tests/generated/packet_decl_64bit_enum_little_endian.rs
+++ b/tests/generated/packet_decl_64bit_enum_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_64bit_scalar_array_big_endian.rs b/tests/generated/packet_decl_64bit_scalar_array_big_endian.rs
index b4816d6..261e4dd 100644
--- a/tests/generated/packet_decl_64bit_scalar_array_big_endian.rs
+++ b/tests/generated/packet_decl_64bit_scalar_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_64bit_scalar_array_little_endian.rs b/tests/generated/packet_decl_64bit_scalar_array_little_endian.rs
index b6c3423..f60c64c 100644
--- a/tests/generated/packet_decl_64bit_scalar_array_little_endian.rs
+++ b/tests/generated/packet_decl_64bit_scalar_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_64bit_scalar_big_endian.rs b/tests/generated/packet_decl_64bit_scalar_big_endian.rs
index 1cda594..e067fac 100644
--- a/tests/generated/packet_decl_64bit_scalar_big_endian.rs
+++ b/tests/generated/packet_decl_64bit_scalar_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_64bit_scalar_little_endian.rs b/tests/generated/packet_decl_64bit_scalar_little_endian.rs
index 10e62d4..815d0b1 100644
--- a/tests/generated/packet_decl_64bit_scalar_little_endian.rs
+++ b/tests/generated/packet_decl_64bit_scalar_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
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 f36f1ba..ba959a6 100644
--- a/tests/generated/packet_decl_8bit_enum_array_big_endian.rs
+++ b/tests/generated/packet_decl_8bit_enum_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 f36f1ba..ba959a6 100644
--- a/tests/generated/packet_decl_8bit_enum_array_little_endian.rs
+++ b/tests/generated/packet_decl_8bit_enum_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_8bit_enum_big_endian.rs b/tests/generated/packet_decl_8bit_enum_big_endian.rs
index bccdaeb..03bec5d 100644
--- a/tests/generated/packet_decl_8bit_enum_big_endian.rs
+++ b/tests/generated/packet_decl_8bit_enum_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_8bit_enum_little_endian.rs b/tests/generated/packet_decl_8bit_enum_little_endian.rs
index bccdaeb..03bec5d 100644
--- a/tests/generated/packet_decl_8bit_enum_little_endian.rs
+++ b/tests/generated/packet_decl_8bit_enum_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_8bit_scalar_array_big_endian.rs b/tests/generated/packet_decl_8bit_scalar_array_big_endian.rs
index 518a775..74099b0 100644
--- a/tests/generated/packet_decl_8bit_scalar_array_big_endian.rs
+++ b/tests/generated/packet_decl_8bit_scalar_array_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_8bit_scalar_array_little_endian.rs b/tests/generated/packet_decl_8bit_scalar_array_little_endian.rs
index 518a775..74099b0 100644
--- a/tests/generated/packet_decl_8bit_scalar_array_little_endian.rs
+++ b/tests/generated/packet_decl_8bit_scalar_array_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_8bit_scalar_big_endian.rs b/tests/generated/packet_decl_8bit_scalar_big_endian.rs
index 19b51a9..de13f31 100644
--- a/tests/generated/packet_decl_8bit_scalar_big_endian.rs
+++ b/tests/generated/packet_decl_8bit_scalar_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_8bit_scalar_little_endian.rs b/tests/generated/packet_decl_8bit_scalar_little_endian.rs
index 19b51a9..de13f31 100644
--- a/tests/generated/packet_decl_8bit_scalar_little_endian.rs
+++ b/tests/generated/packet_decl_8bit_scalar_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_array_dynamic_count_big_endian.rs b/tests/generated/packet_decl_array_dynamic_count_big_endian.rs
index e1887d6..074443c 100644
--- a/tests/generated/packet_decl_array_dynamic_count_big_endian.rs
+++ b/tests/generated/packet_decl_array_dynamic_count_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_array_dynamic_count_little_endian.rs b/tests/generated/packet_decl_array_dynamic_count_little_endian.rs
index a7fcdd1..e6429b4 100644
--- a/tests/generated/packet_decl_array_dynamic_count_little_endian.rs
+++ b/tests/generated/packet_decl_array_dynamic_count_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_array_dynamic_size_big_endian.rs b/tests/generated/packet_decl_array_dynamic_size_big_endian.rs
index 934b21f..a4899a1 100644
--- a/tests/generated/packet_decl_array_dynamic_size_big_endian.rs
+++ b/tests/generated/packet_decl_array_dynamic_size_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_array_dynamic_size_little_endian.rs b/tests/generated/packet_decl_array_dynamic_size_little_endian.rs
index d56c554..d048bfc 100644
--- a/tests/generated/packet_decl_array_dynamic_size_little_endian.rs
+++ b/tests/generated/packet_decl_array_dynamic_size_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_big_endian.rs b/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_big_endian.rs
index 0ddc911..d542792 100644
--- a/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_big_endian.rs
+++ b/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs b/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs
index 86a85e4..767671f 100644
--- a/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs
+++ b/tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs b/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs
index bd67530..63d2dc2 100644
--- a/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs
+++ b/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs b/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs
index c41a8d4..30ef3ba 100644
--- a/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs
+++ b/tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_array_with_padding_big_endian.rs b/tests/generated/packet_decl_array_with_padding_big_endian.rs
index e45dc03..0231b64 100644
--- a/tests/generated/packet_decl_array_with_padding_big_endian.rs
+++ b/tests/generated/packet_decl_array_with_padding_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_array_with_padding_little_endian.rs b/tests/generated/packet_decl_array_with_padding_little_endian.rs
index bd1aae0..6aee602 100644
--- a/tests/generated/packet_decl_array_with_padding_little_endian.rs
+++ b/tests/generated/packet_decl_array_with_padding_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/packet_decl_child_packets_big_endian.rs b/tests/generated/packet_decl_child_packets_big_endian.rs
index 8b3e05d..3498cae 100644
--- a/tests/generated/packet_decl_child_packets_big_endian.rs
+++ b/tests/generated/packet_decl_child_packets_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_child_packets_little_endian.rs b/tests/generated/packet_decl_child_packets_little_endian.rs
index 8a464b2..cebc67e 100644
--- a/tests/generated/packet_decl_child_packets_little_endian.rs
+++ b/tests/generated/packet_decl_child_packets_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_complex_scalars_big_endian.rs b/tests/generated/packet_decl_complex_scalars_big_endian.rs
index be97361..4c3de07 100644
--- a/tests/generated/packet_decl_complex_scalars_big_endian.rs
+++ b/tests/generated/packet_decl_complex_scalars_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_complex_scalars_little_endian.rs b/tests/generated/packet_decl_complex_scalars_little_endian.rs
index 7ccee61..4bd1a80 100644
--- a/tests/generated/packet_decl_complex_scalars_little_endian.rs
+++ b/tests/generated/packet_decl_complex_scalars_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_custom_field_big_endian.rs b/tests/generated/packet_decl_custom_field_big_endian.rs
index 7b7dc9a..43b2237 100644
--- a/tests/generated/packet_decl_custom_field_big_endian.rs
+++ b/tests/generated/packet_decl_custom_field_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serde", serde(try_from = "u32", into = "u32"))]
diff --git a/tests/generated/packet_decl_custom_field_little_endian.rs b/tests/generated/packet_decl_custom_field_little_endian.rs
index d805403..f1b9d0a 100644
--- a/tests/generated/packet_decl_custom_field_little_endian.rs
+++ b/tests/generated/packet_decl_custom_field_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 #[cfg_attr(feature = "serde", serde(try_from = "u32", into = "u32"))]
diff --git a/tests/generated/packet_decl_empty_big_endian.rs b/tests/generated/packet_decl_empty_big_endian.rs
index eb0b173..49f14b8 100644
--- a/tests/generated/packet_decl_empty_big_endian.rs
+++ b/tests/generated/packet_decl_empty_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {}
diff --git a/tests/generated/packet_decl_empty_little_endian.rs b/tests/generated/packet_decl_empty_little_endian.rs
index eb0b173..49f14b8 100644
--- a/tests/generated/packet_decl_empty_little_endian.rs
+++ b/tests/generated/packet_decl_empty_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {}
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 5c780ad..78b2b99 100644
--- a/tests/generated/packet_decl_fixed_enum_field_big_endian.rs
+++ b/tests/generated/packet_decl_fixed_enum_field_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 45486c5..dfb4c25 100644
--- a/tests/generated/packet_decl_fixed_enum_field_little_endian.rs
+++ b/tests/generated/packet_decl_fixed_enum_field_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 4d0ecb5..2c5ad70 100644
--- a/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs
+++ b/tests/generated/packet_decl_fixed_scalar_field_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
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 5d4ba13..b37bce0 100644
--- a/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs
+++ b/tests/generated/packet_decl_fixed_scalar_field_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_grand_children_big_endian.rs b/tests/generated/packet_decl_grand_children_big_endian.rs
index f1a2cac..64ca83f 100644
--- a/tests/generated/packet_decl_grand_children_big_endian.rs
+++ b/tests/generated/packet_decl_grand_children_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_grand_children_little_endian.rs b/tests/generated/packet_decl_grand_children_little_endian.rs
index 66fa76a..9c03220 100644
--- a/tests/generated/packet_decl_grand_children_little_endian.rs
+++ b/tests/generated/packet_decl_grand_children_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_mask_scalar_value_big_endian.rs b/tests/generated/packet_decl_mask_scalar_value_big_endian.rs
index 47f7a70..dddbc0b 100644
--- a/tests/generated/packet_decl_mask_scalar_value_big_endian.rs
+++ b/tests/generated/packet_decl_mask_scalar_value_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_mask_scalar_value_little_endian.rs b/tests/generated/packet_decl_mask_scalar_value_little_endian.rs
index 95f6c9d..57bcb51 100644
--- a/tests/generated/packet_decl_mask_scalar_value_little_endian.rs
+++ b/tests/generated/packet_decl_mask_scalar_value_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
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 7dcf2e6..1bf676b 100644
--- a/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs
+++ b/tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 667dab3..baa27e3 100644
--- a/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs
+++ b/tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 785c0ee..c0a1cf1 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
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 785c0ee..c0a1cf1 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
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 82c45c5..4bb083b 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
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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 82c45c5..4bb083b 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
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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>;
-}
 #[repr(u64)]
 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
diff --git a/tests/generated/packet_decl_payload_field_unknown_size_big_endian.rs b/tests/generated/packet_decl_payload_field_unknown_size_big_endian.rs
index be55b05..bfbffe7 100644
--- a/tests/generated/packet_decl_payload_field_unknown_size_big_endian.rs
+++ b/tests/generated/packet_decl_payload_field_unknown_size_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs b/tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs
index bc20979..e473774 100644
--- a/tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs
+++ b/tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs b/tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs
index a8af260..c37ff2b 100644
--- a/tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs
+++ b/tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_payload_field_unknown_size_terminal_little_endian.rs b/tests/generated/packet_decl_payload_field_unknown_size_terminal_little_endian.rs
index c615757..2e22410 100644
--- a/tests/generated/packet_decl_payload_field_unknown_size_terminal_little_endian.rs
+++ b/tests/generated/packet_decl_payload_field_unknown_size_terminal_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_payload_field_variable_size_big_endian.rs b/tests/generated/packet_decl_payload_field_variable_size_big_endian.rs
index ea6f7bf..5df7232 100644
--- a/tests/generated/packet_decl_payload_field_variable_size_big_endian.rs
+++ b/tests/generated/packet_decl_payload_field_variable_size_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_payload_field_variable_size_little_endian.rs b/tests/generated/packet_decl_payload_field_variable_size_little_endian.rs
index f2932f0..477c8b6 100644
--- a/tests/generated/packet_decl_payload_field_variable_size_little_endian.rs
+++ b/tests/generated/packet_decl_payload_field_variable_size_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 enum FooDataChild {
diff --git a/tests/generated/packet_decl_reserved_field_big_endian.rs b/tests/generated/packet_decl_reserved_field_big_endian.rs
index 6d2a01e..b296e58 100644
--- a/tests/generated/packet_decl_reserved_field_big_endian.rs
+++ b/tests/generated/packet_decl_reserved_field_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {}
diff --git a/tests/generated/packet_decl_reserved_field_little_endian.rs b/tests/generated/packet_decl_reserved_field_little_endian.rs
index 6d2a01e..b296e58 100644
--- a/tests/generated/packet_decl_reserved_field_little_endian.rs
+++ b/tests/generated/packet_decl_reserved_field_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {}
diff --git a/tests/generated/packet_decl_simple_scalars_big_endian.rs b/tests/generated/packet_decl_simple_scalars_big_endian.rs
index f2048d9..8c31583 100644
--- a/tests/generated/packet_decl_simple_scalars_big_endian.rs
+++ b/tests/generated/packet_decl_simple_scalars_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/packet_decl_simple_scalars_little_endian.rs b/tests/generated/packet_decl_simple_scalars_little_endian.rs
index 16f01f7..d1c3655 100644
--- a/tests/generated/packet_decl_simple_scalars_little_endian.rs
+++ b/tests/generated/packet_decl_simple_scalars_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 FooData {
diff --git a/tests/generated/preamble.rs b/tests/generated/preamble.rs
index e3e9772..dd11680 100644
--- a/tests/generated/preamble.rs
+++ b/tests/generated/preamble.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,28 +18,3 @@
         &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>;
-}
diff --git a/tests/generated/reserved_identifier_big_endian.rs b/tests/generated/reserved_identifier_big_endian.rs
index 1f13532..f346dd1 100644
--- a/tests/generated/reserved_identifier_big_endian.rs
+++ b/tests/generated/reserved_identifier_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 {
diff --git a/tests/generated/reserved_identifier_little_endian.rs b/tests/generated/reserved_identifier_little_endian.rs
index 1f13532..f346dd1 100644
--- a/tests/generated/reserved_identifier_little_endian.rs
+++ b/tests/generated/reserved_identifier_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 {
diff --git a/tests/generated/struct_decl_complex_scalars_big_endian.rs b/tests/generated/struct_decl_complex_scalars_big_endian.rs
index a8de9c9..5fa18cd 100644
--- a/tests/generated/struct_decl_complex_scalars_big_endian.rs
+++ b/tests/generated/struct_decl_complex_scalars_big_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/generated/struct_decl_complex_scalars_little_endian.rs b/tests/generated/struct_decl_complex_scalars_little_endian.rs
index e0741f7..dbd7b69 100644
--- a/tests/generated/struct_decl_complex_scalars_little_endian.rs
+++ b/tests/generated/struct_decl_complex_scalars_little_endian.rs
@@ -4,7 +4,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::cell::Cell;
 use std::fmt;
-use thiserror::Error;
+use pdl_runtime::{Error, Packet};
 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.
@@ -18,31 +18,6 @@
         &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 Foo {
diff --git a/tests/python_generator_test.py b/tests/python_generator_test.py
index fdd04a8..a126c8d 100644
--- a/tests/python_generator_test.py
+++ b/tests/python_generator_test.py
@@ -63,7 +63,7 @@
     elif typing_extensions.get_origin(typ) is typing.Union:
         # typing.Optional[int] expands to typing.Union[int, None]
         typ = typing_extensions.get_args(typ)[0]
-        return create_object(typ, value) if value else None
+        return create_object(typ, value) if value is not None else None
     elif typ is bytes:
         return bytes(value)
     elif typ is bytearray:
diff --git a/tests/run_cxx_generator_tests.sh b/tests/run_cxx_generator_tests.sh
index d595747..fa90323 100755
--- a/tests/run_cxx_generator_tests.sh
+++ b/tests/run_cxx_generator_tests.sh
@@ -3,44 +3,48 @@
 set -euxo pipefail
 
 mkdir -p out/
+OUT_DIR="$(pwd)/out"
+
+# move to `pdl-compiler` directory
+cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." &> /dev/null
 
 sed -e 's/little_endian_packets/big_endian_packets/' \
     -e '/Start: little_endian_only/,/End: little_endian_only/d' \
-    < tests/canonical/le_test_file.pdl > out/be_test_file.pdl
+    < tests/canonical/le_test_file.pdl > "$OUT_DIR"/be_test_file.pdl
 
-pdlc tests/canonical/le_test_file.pdl > out/le_test_file.json
-pdlc out/be_test_file.pdl > out/be_test_file.json
+pdlc tests/canonical/le_test_file.pdl > "$OUT_DIR"/le_test_file.json
+pdlc "$OUT_DIR"/be_test_file.pdl > "$OUT_DIR"/be_test_file.json
 
 python3 scripts/generate_cxx_backend.py \
-    --input out/le_test_file.json \
-    --output out/le_backend.h \
+    --input "$OUT_DIR"/le_test_file.json \
+    --output "$OUT_DIR"/le_backend.h \
     --namespace le_backend
 python3 scripts/generate_cxx_backend.py \
-    --input out/be_test_file.json \
-    --output out/be_backend.h \
+    --input "$OUT_DIR"/be_test_file.json \
+    --output "$OUT_DIR"/be_backend.h \
     --namespace be_backend
 
 python3 scripts/generate_cxx_backend_tests.py \
-    --input out/le_test_file.json \
-    --output out/le_backend_tests.cc \
+    --input "$OUT_DIR"/le_test_file.json \
+    --output "$OUT_DIR"/le_backend_tests.cc \
     --test-vectors tests/canonical/le_test_vectors.json \
     --namespace le_backend \
     --parser-test-suite le_backend_parser_test \
     --serializer-test-suite le_backend_serializer_test \
     --include-header le_backend.h
 python3 scripts/generate_cxx_backend_tests.py \
-    --input out/be_test_file.json \
-    --output out/be_backend_tests.cc \
+    --input "$OUT_DIR"/be_test_file.json \
+    --output "$OUT_DIR"/be_backend_tests.cc \
     --test-vectors tests/canonical/be_test_vectors.json \
     --namespace be_backend \
     --parser-test-suite be_backend_parser_test \
     --serializer-test-suite be_backend_serializer_test \
     --include-header be_backend.h
 
-g++ -Iscripts -Iout \
-    out/le_backend_tests.cc \
-    out/be_backend_tests.cc \
-    -lgtest -lgtest_main -o out/cxx_backend_tests
+g++ -Iscripts -I"$OUT_DIR" \
+    "$OUT_DIR"/le_backend_tests.cc \
+    "$OUT_DIR"/be_backend_tests.cc \
+    -lgtest -lgtest_main -o "$OUT_DIR"/cxx_backend_tests
 
-./out/cxx_backend_tests \
-    --gtest_output="xml:out/cxx_backend_tests_detail.xml"
+"$OUT_DIR"/cxx_backend_tests \
+    --gtest_output="xml:$OUT_DIR/cxx_backend_tests_detail.xml"
diff --git a/tests/run_python_generator_tests.sh b/tests/run_python_generator_tests.sh
index 64b39c6..e11b8f5 100755
--- a/tests/run_python_generator_tests.sh
+++ b/tests/run_python_generator_tests.sh
@@ -3,22 +3,26 @@
 set -euxo pipefail
 
 mkdir -p out/
+OUT_DIR="$(pwd)/out"
+
+# move to `pdl-compiler` directory
+cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." &> /dev/null
 
 sed -e 's/little_endian_packets/big_endian_packets/' \
     -e '/Start: little_endian_only/,/End: little_endian_only/d' \
-    < tests/canonical/le_test_file.pdl > out/be_test_file.pdl
+    < tests/canonical/le_test_file.pdl > "$OUT_DIR"/be_test_file.pdl
 
-pdlc tests/canonical/le_test_file.pdl > out/le_test_file.json
-pdlc out/be_test_file.pdl > out/be_test_file.json
+pdlc tests/canonical/le_test_file.pdl > "$OUT_DIR"/le_test_file.json
+pdlc "$OUT_DIR"/be_test_file.pdl > "$OUT_DIR"/be_test_file.json
 
 python3 scripts/generate_python_backend.py \
-    --input out/le_test_file.json \
-    --output out/le_backend.py \
+    --input "$OUT_DIR"/le_test_file.json \
+    --output "$OUT_DIR"/le_backend.py \
     --custom-type-location tests.custom_types
 python3 scripts/generate_python_backend.py \
-    --input out/be_test_file.json \
-    --output out/be_backend.py \
+    --input "$OUT_DIR"/be_test_file.json \
+    --output "$OUT_DIR"/be_backend.py \
     --custom-type-location tests.custom_types
 
-export PYTHONPATH="./out:.:${PYTHONPATH:-}"
+export PYTHONPATH="$OUT_DIR:.:${PYTHONPATH:-}"
 python3 tests/python_generator_test.py
diff --git a/tests/run_rust_generator_tests.sh b/tests/run_rust_generator_tests.sh
new file mode 100755
index 0000000..5e8ecf1
--- /dev/null
+++ b/tests/run_rust_generator_tests.sh
@@ -0,0 +1,103 @@
+#!/usr/bin/env bash
+
+set -euxo pipefail
+
+mkdir -p out/
+OUT_DIR="$(pwd)/out"
+
+# move to `pdl-compiler` directory
+cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." &> /dev/null
+
+sed -e 's/little_endian_packets/big_endian_packets/' \
+    -e '/Start: little_endian_only/,/End: little_endian_only/d' \
+    < tests/canonical/le_test_file.pdl > "$OUT_DIR/be_test_file.pdl"
+
+mkdir -p "$OUT_DIR/canonical_test/src"
+
+cargo run --bin pdlc -- \
+    tests/canonical/le_test_file.pdl \
+    --output-format rust \
+    --exclude-declaration UnsizedCustomField \
+    --exclude-declaration Packet_Custom_Field_VariableSize \
+    --exclude-declaration Struct_Custom_Field_VariableSize_ \
+    --exclude-declaration Struct_Custom_Field_VariableSize \
+    --exclude-declaration Checksum \
+    --exclude-declaration Packet_Checksum_Field_FromStart \
+    --exclude-declaration Packet_Checksum_Field_FromEnd \
+    --exclude-declaration Struct_Checksum_Field_FromStart_ \
+    --exclude-declaration Struct_Checksum_Field_FromStart \
+    --exclude-declaration Struct_Checksum_Field_FromEnd_ \
+    --exclude-declaration Struct_Checksum_Field_FromEnd \
+    --exclude-declaration PartialParent5 \
+    --exclude-declaration PartialParent12 \
+    --exclude-declaration PartialChild5_A \
+    --exclude-declaration PartialChild5_B \
+    --exclude-declaration PartialChild12_A \
+    --exclude-declaration PartialChild12_B \
+    --exclude-declaration Packet_Payload_Field_SizeModifier \
+    --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \
+    --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \
+    --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \
+    > "$OUT_DIR/canonical_test/src/le_backend.rs"
+cargo run --bin pdlc -- \
+    "$OUT_DIR/be_test_file.pdl" \
+    --output-format rust \
+    --exclude-declaration UnsizedCustomField \
+    --exclude-declaration Packet_Custom_Field_VariableSize \
+    --exclude-declaration Struct_Custom_Field_VariableSize_ \
+    --exclude-declaration Struct_Custom_Field_VariableSize \
+    --exclude-declaration Checksum \
+    --exclude-declaration Packet_Checksum_Field_FromStart \
+    --exclude-declaration Packet_Checksum_Field_FromEnd \
+    --exclude-declaration Struct_Checksum_Field_FromStart_ \
+    --exclude-declaration Struct_Checksum_Field_FromStart \
+    --exclude-declaration Struct_Checksum_Field_FromEnd_ \
+    --exclude-declaration Struct_Checksum_Field_FromEnd \
+    --exclude-declaration Packet_Payload_Field_SizeModifier \
+    --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \
+    --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \
+    --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \
+    > "$OUT_DIR/canonical_test/src/be_backend.rs"
+cargo run --bin generate-canonical-tests -- \
+    tests/canonical/le_test_vectors.json "crate::le_backend" \
+    > "$OUT_DIR/canonical_test/src/le_backend_tests.rs"
+cargo run --bin generate-canonical-tests -- \
+    tests/canonical/be_test_vectors.json "crate::be_backend" \
+    > "$OUT_DIR/canonical_test/src/be_backend_tests.rs"
+
+
+cat <<EOT > "$OUT_DIR/canonical_test/src/lib.rs"
+mod le_backend;
+mod le_backend_tests;
+mod be_backend;
+mod be_backend_tests;
+EOT
+
+cat <<EOT > "$OUT_DIR/canonical_test/Cargo.toml"
+[package]
+name = "canonical_test"
+version = "0.0.0"
+publish = false
+edition = "2021"
+
+[features]
+default = ["serde"]
+
+[dependencies]
+bytes = {version = "1.4.0", features = ["serde"]}
+thiserror = "1.0.47"
+serde_json = "1.0.86"
+
+[dependencies.serde]
+version = "1.0.145"
+features = ["default", "derive", "serde_derive", "std", "rc"]
+optional = true
+
+[dependencies.pdl-runtime]
+path = "../../pdl-runtime"
+
+[workspace]
+EOT
+
+cd "$OUT_DIR/canonical_test"
+RUSTFLAGS=-Awarnings cargo test --tests