Repository: sharksforarms/deku (rust)

Issue:
`read_all` attribute
Currently there are the `count`, `bytes_read`, `bits_read` and `until` attributes you can apply to a `Vec` to tell deku how much to read.

A `read_all` (name undecided) attribute that tells deku to parse till the end of the input would be very handy and could be used to avoid some ugly math in the `bytes_read` attribute.

Pull request description:
Add read_all attribute
Add read_all attribute to read until the reader.end() returns true

Fix couple of doc bugs

Closes #230 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3abe93f..a18b9e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ## Changes
 [#390](https://github.com/sharksforarms/deku/pull/390) added MSRV for `1.67.1`.
+[#389](https://github.com/sharksforarms/deku/pull/389) changed edition to 2021
 [#352](https://github.com/sharksforarms/deku/pull/352) added a new function `from_reader` that uses `io::Read`.
 `io::Read` is also now used internally, bringing massive performance and usability improvements.
 
diff --git a/Cargo.toml b/Cargo.toml
index 6170c72..21c42b1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 authors = ["sharks <sharks@sharks>"]
-edition = "2018"
+edition = "2021"
 name = "deku"
 version = "0.16.0"
 license = "MIT OR Apache-2.0"
diff --git a/README.md b/README.md
index 4a215b6..e8dd4fb 100644
--- a/README.md
+++ b/README.md
@@ -44,9 +44,9 @@ use deku::prelude::*;
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 #[deku(endian = "big")]
 struct DekuTest {
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     field_a: u8,
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     field_b: u8,
     field_c: u16,
 }
diff --git a/benches/deku.rs b/benches/deku.rs
index 4243ff5..d6bad65 100644
--- a/benches/deku.rs
+++ b/benches/deku.rs
@@ -5,11 +5,11 @@ use deku::prelude::*;
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 struct DekuBits {
-    #[deku(bits = "1")]
+    #[deku(bits = 1)]
     data_01: u8,
-    #[deku(bits = "2")]
+    #[deku(bits = 2)]
     data_02: u8,
-    #[deku(bits = "5")]
+    #[deku(bits = 5)]
     data_03: u8,
 }
 
diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs
index 87e28f4..a2a0de3 100644
--- a/deku-derive/src/lib.rs
+++ b/deku-derive/src/lib.rs
@@ -23,6 +23,7 @@ mod macros;
 enum Id {
     TokenStream(TokenStream),
     LitByteStr(syn::LitByteStr),
+    Int(syn::LitInt),
 }
 
 impl ToString for Id {
@@ -36,6 +37,7 @@ impl ToTokens for Id {
         match self {
             Id::TokenStream(v) => v.to_tokens(tokens),
             Id::LitByteStr(v) => v.to_tokens(tokens),
+            Id::Int(v) => v.to_tokens(tokens),
         }
     }
 }
@@ -49,6 +51,7 @@ impl FromMeta for Id {
                     .parse::<TokenStream>()
                     .expect("could not parse token stream"),
             )),
+            syn::Lit::Int(ref s) => Ok(Id::Int(s.clone())),
             syn::Lit::ByteStr(ref s) => Ok(Id::LitByteStr(s.clone())),
             _ => Err(darling::Error::unexpected_lit_type(value)),
         })
@@ -387,6 +390,9 @@ struct FieldData {
     /// a predicate to decide when to stop reading elements into the container
     until: Option<TokenStream>,
 
+    /// read until `reader.end()`
+    read_all: bool,
+
     /// apply a function to the field after it's read
     map: Option<TokenStream>,
 
@@ -454,6 +460,7 @@ impl FieldData {
             bits_read: receiver.bits_read?,
             bytes_read: receiver.bytes_read?,
             until: receiver.until?,
+            read_all: receiver.read_all,
             map: receiver.map?,
             ctx,
             update: receiver.update?,
@@ -521,6 +528,18 @@ impl FieldData {
             ));
         }
 
+        // Validate usage of read_all
+        if data.read_all
+            && (data.until.is_some()
+                || data.count.is_some()
+                || (data.bits_read.is_some() || data.bytes_read.is_some()))
+        {
+            return Err(cerror(
+                data.bits.span(),
+                "conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`",
+            ));
+        }
+
         Ok(())
     }
 
@@ -759,6 +778,10 @@ struct DekuFieldReceiver {
     #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
     until: Result<Option<TokenStream>, ReplacementError>,
 
+    /// read until `reader.end()`
+    #[darling(default)]
+    read_all: bool,
+
     /// apply a function to the field after it's read
     #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
     map: Result<Option<TokenStream>, ReplacementError>,
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index b075355..5d76c3f 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -4,7 +4,6 @@ use darling::ast::{Data, Fields};
 use darling::ToTokens;
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::spanned::Spanned;
 
 use crate::macros::{
     gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, gen_internal_field_idents,
@@ -183,6 +182,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             match variant_id {
                 Id::TokenStream(v) => (false, quote! {&#v}.into_token_stream()),
                 Id::LitByteStr(v) => (false, v.into_token_stream()),
+                Id::Int(v) => (false, v.into_token_stream()),
             }
         } else if let Some(variant_id_pat) = &variant.id_pat {
             // If user has supplied an id, then we have an id_pat that and the id variant doesn't
@@ -234,6 +234,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             if let Some(variant_id) = &variant.id {
                 let deref = match variant_id {
                     Id::TokenStream(_) => quote! {},
+                    Id::Int(_) => quote! {},
                     Id::LitByteStr(_) => quote! {*},
                 };
 
@@ -395,12 +396,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
     let deku_id_type = if let Some(id_type) = id_type {
         Some(quote! {#id_type})
     } else if let (Some(ctx), Some(id)) = (input.ctx.as_ref(), input.id.as_ref()) {
-        Some(gen_type_from_ctx_id(ctx, id).ok_or_else(|| {
-            syn::Error::new(
-                id.span(),
-                "DekuReader: cannot determine `id` type from `ctx`",
-            )
-        })?)
+        gen_type_from_ctx_id(ctx, id)
     } else {
         None
     };
@@ -683,6 +679,17 @@ fn emit_field_read(
                     (::#crate_::ctx::Limit::new_until(#field_until), (#read_args))
                 )?
             }
+        } else if f.read_all {
+            quote! {
+                {
+                    use core::borrow::Borrow;
+                    #type_as_deku_read::from_reader_with_ctx
+                    (
+                        __deku_reader,
+                        (::#crate_::ctx::Limit::end(), (#read_args))
+                    )?
+                }
+            }
         } else {
             quote! {
                 #type_as_deku_read::from_reader_with_ctx
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 69243a5..6e0b715 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -203,6 +203,12 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                             __deku_variant_id.write(__deku_output, (#id_args))?;
                         }
                     }
+                    Id::Int(v) => {
+                        quote! {
+                            let mut __deku_variant_id: #id_type = #v;
+                            __deku_variant_id.write(__deku_output, (#id_args))?;
+                        }
+                    }
                     Id::LitByteStr(v) => {
                         quote! {
                             let mut __deku_variant_id: #id_type = *#v;
diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs
index 3dccf46..2fa865c 100644
--- a/deku-derive/src/macros/mod.rs
+++ b/deku-derive/src/macros/mod.rs
@@ -199,7 +199,9 @@ fn gen_type_from_ctx_id(
     id: &crate::Id,
 ) -> Option<TokenStream> {
     let parser = Punctuated::<Ident, Comma>::parse_terminated;
-    let s = parser.parse(id.to_token_stream().into()).unwrap();
+    let Ok(s) = parser.parse(id.to_token_stream().into()) else {
+        return None;
+    };
     let mut matching_types = quote! {};
     for s in s {
         let id = syn::Ident::new(&s.to_string(), id.span());
@@ -232,8 +234,8 @@ fn gen_type_from_ctx_id(
 }
 
 /// Generate argument for `id`:
-/// `#deku(endian = "big", bits = "1")` -> `Endian::Big, BitSize(1)`
-/// `#deku(endian = "big", bytes = "1")` -> `Endian::Big, ByteSize(1)`
+/// `#deku(endian = "big", bits = 1)` -> `Endian::Big, BitSize(1)`
+/// `#deku(endian = "big", bytes = 1)` -> `Endian::Big, ByteSize(1)`
 pub(crate) fn gen_id_args(
     endian: Option<&syn::LitStr>,
     bits: Option<&Num>,
@@ -258,8 +260,8 @@ pub(crate) fn gen_id_args(
 
 /// Generate argument for fields:
 ///
-/// `#deku(endian = "big", bits = "1", ctx = "a")` -> `Endian::Big, BitSize(1), a`
-/// `#deku(endian = "big", bytes = "1", ctx = "a")` -> `Endian::Big, ByteSize(1), a`
+/// `#deku(endian = "big", bits = 1, ctx = "a")` -> `Endian::Big, BitSize(1), a`
+/// `#deku(endian = "big", bytes = 1, ctx = "a")` -> `Endian::Big, ByteSize(1), a`
 fn gen_field_args(
     endian: Option<&syn::LitStr>,
     bits: Option<&Num>,
diff --git a/ensure_no_std/src/bin/main.rs b/ensure_no_std/src/bin/main.rs
index 8bf3102..fdaf576 100644
--- a/ensure_no_std/src/bin/main.rs
+++ b/ensure_no_std/src/bin/main.rs
@@ -17,9 +17,9 @@ use deku::prelude::*;
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 struct DekuTest {
-    #[deku(bits = "5")]
+    #[deku(bits = 5)]
     field_a: u8,
-    #[deku(bits = "3")]
+    #[deku(bits = 3)]
     field_b: u8,
     count: u8,
     #[deku(count = "count")]
diff --git a/ensure_wasm/src/lib.rs b/ensure_wasm/src/lib.rs
index 539f94e..bbcbdfa 100644
--- a/ensure_wasm/src/lib.rs
+++ b/ensure_wasm/src/lib.rs
@@ -25,9 +25,9 @@ use deku::prelude::*;
 #[wasm_bindgen]
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 pub struct DekuTest {
-    #[deku(bits = "5")]
+    #[deku(bits = 5)]
     pub field_a: u8,
-    #[deku(bits = "3")]
+    #[deku(bits = 3)]
     pub field_b: u8,
     pub field_c: u8,
 }
diff --git a/examples/enums.rs b/examples/enums.rs
index 9286c15..296ffc5 100644
--- a/examples/enums.rs
+++ b/examples/enums.rs
@@ -3,25 +3,33 @@ use std::io::Cursor;
 use deku::{prelude::*, reader::Reader};
 use hexlit::hex;
 
+const THREE: u8 = 3;
+
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 #[deku(type = "u8")]
 enum DekuTest {
-    #[deku(id = "0")]
+    #[deku(id = 0)]
     Var1,
-    #[deku(id = "1")]
-    Var2(#[deku(bytes = "2")] u32),
-    #[deku(id = "2")]
+
+    #[deku(id = 1)]
+    Var2(#[deku(bytes = 2)] u32),
+
+    #[deku(id = 0x02)]
     Var3(u8, u8),
-    #[deku(id = "3")]
+
+    #[deku(id = "THREE")]
     Var4 {
         field_a: u8,
         #[deku(count = "field_a")]
         field_b: Vec<u8>,
     },
+
     #[deku(id_pat = "4..=6")]
     Var5 { id: u8 },
+
     #[deku(id_pat = "&id if id > 6")]
     Var6 { id: u8 },
+
     #[deku(id_pat = "_")]
     VarDefault { id: u8, value: u8 },
 }
diff --git a/examples/example.rs b/examples/example.rs
index eae2f91..2f7d9f6 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -11,7 +11,7 @@ use deku::prelude::*;
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 struct FieldF {
-    #[deku(bits = "6")]
+    #[deku(bits = 6)]
     #[deku(assert_eq = "6")]
     data: u8,
 }
@@ -26,13 +26,13 @@ struct FieldF {
 // #[deku(endian = "little")] // By default it uses the system endianness, but can be overwritten
 struct DekuTest {
     field_a: u8,
-    #[deku(bits = "7")]
+    #[deku(bits = 7)]
     field_b: u8,
-    #[deku(bits = "1")]
+    #[deku(bits = 1)]
     field_c: u8,
     #[deku(endian = "big")]
     field_d: u16,
-    #[deku(bits = "2")]
+    #[deku(bits = 2)]
     field_e: u8,
     field_f: FieldF,
     num_items: u8,
diff --git a/examples/ipv4.rs b/examples/ipv4.rs
index a14e1c3..2cfb76b 100644
--- a/examples/ipv4.rs
+++ b/examples/ipv4.rs
@@ -25,19 +25,19 @@ use hexlit::hex;
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 #[deku(endian = "big")]
 pub struct Ipv4Header {
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     pub version: u8, // Version
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     pub ihl: u8, // Internet Header Length
-    #[deku(bits = "6")]
+    #[deku(bits = 6)]
     pub dscp: u8, // Differentiated Services Code Point
-    #[deku(bits = "2")]
+    #[deku(bits = 2)]
     pub ecn: u8, // Explicit Congestion Notification
     pub length: u16,         // Total Length
     pub identification: u16, // Identification
-    #[deku(bits = "3")]
+    #[deku(bits = 3)]
     pub flags: u8, // Flags
-    #[deku(bits = "13")]
+    #[deku(bits = 13)]
     pub offset: u16, // Fragment Offset
     pub ttl: u8,             // Time To Live
     pub protocol: u8,        // Protocol
diff --git a/examples/read_all.rs b/examples/read_all.rs
new file mode 100644
index 0000000..b3c57f8
--- /dev/null
+++ b/examples/read_all.rs
@@ -0,0 +1,23 @@
+use deku::prelude::*;
+use std::convert::{TryFrom, TryInto};
+
+fn main() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct {
+        #[deku(read_all)]
+        data: Vec<(u8, u8)>,
+    }
+
+    let test_data: Vec<u8> = [0xaa, 0xbb, 0xcc, 0xdd].to_vec();
+
+    let ret_read = TestStruct::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct {
+            data: vec![(0xaa, 0xbb), (0xcc, 0xdd)]
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+}
diff --git a/src/attributes.rs b/src/attributes.rs
index 16b0028..ee596a0 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -42,6 +42,7 @@ enum DekuEnum {
 | [bits_read](#bits_read) | field | Set the field representing the number of bits to read into a container
 | [bytes_read](#bytes_read) | field | Set the field representing the number of bytes to read into a container
 | [until](#until) | field | Set a predicate returning when to stop reading elements into a container
+| [read_all](#read_all) | field | Read until [reader.end()] returns `true`
 | [update](#update) | field | Apply code over the field when `.update()` is called
 | [temp](#temp) | field | Read the field but exclude it from the struct/enum
 | [temp_value](#temp_value) | field | Write the field but exclude it from the struct/enum
@@ -412,6 +413,43 @@ assert_eq!(
     },
     value
 );
+
+```
+# read_all
+
+Read values into the container until [reader.end()] returns `true`.
+
+Example:
+```rust
+# use deku::prelude::*;
+# use std::convert::{TryInto, TryFrom};
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct InnerDekuTest {
+    field_a: u8,
+    field_b: u8
+}
+
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    #[deku(read_all)]
+    items: Vec<InnerDekuTest>,
+}
+
+let data: &[u8] = &[0xAB, 0xBC, 0xDE, 0xEF];
+
+let value = DekuTest::try_from(data).unwrap();
+
+assert_eq!(
+    DekuTest {
+       items: vec![
+           InnerDekuTest{field_a: 0xAB, field_b: 0xBC},
+           InnerDekuTest{field_a: 0xDE, field_b: 0xEF}],
+    },
+    value
+);
+
+let value: Vec<u8> = value.try_into().unwrap();
+assert_eq!(&*data, value);
 ```
 
 
@@ -887,7 +925,7 @@ assert_eq!(value.sub.b, 0x01 + 0x02)
 Example:
 ```ignore
 struct Type1 {
-    #[deku(endian = "big", bits = "1")]
+    #[deku(endian = "big", bits = 1)]
     field: u8,
 }
 
@@ -904,7 +942,7 @@ Example: Adding context
 #[deku(endian = "big")]
 struct Type1 {
     field_a: u16,
-    #[deku(bits = "5", ctx = "*field_a")]
+    #[deku(bits = 5, ctx = "*field_a")]
     field_b: SubType,
 }
 
@@ -982,9 +1020,9 @@ struct DekuTest {
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 #[deku(ctx = "my_id: u8", id = "my_id")]
 enum MyEnum {
-    #[deku(id = "1")]
+    #[deku(id = 1)]
     VariantA(u8),
-    #[deku(id = "2")]
+    #[deku(id = 2)]
     VariantB,
 }
 
@@ -1021,9 +1059,9 @@ Example:
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 #[deku(type = "u8")]
 enum DekuTest {
-    #[deku(id = "0x01")]
+    #[deku(id = 0x01)]
     VariantA(u8),
-    #[deku(id = "0x02")]
+    #[deku(id = 0x02)]
     VariantB(u8, u16),
 }
 
@@ -1101,7 +1139,7 @@ Example:
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 #[deku(type = "u8")]
 enum DekuTest {
-    #[deku(id = "0x01")]
+    #[deku(id = 0x01)]
     VariantA(u8),
     #[deku(id_pat = "0x02..=0x06")]
     VariantB {
@@ -1151,10 +1189,10 @@ Example:
 # use std::io::Cursor;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8", bits = "4")]
+#[deku(type = "u8", bits = 4)]
 enum DekuTest {
-    #[deku(id = "0b1001")]
-    VariantA( #[deku(bits = "4")] u8, u8),
+    #[deku(id = 0b1001)]
+    VariantA( #[deku(bits = 4)] u8, u8),
 }
 
 let data: &[u8] = &[0b1001_0110, 0xFF];
@@ -1182,9 +1220,9 @@ Example:
 # use deku::prelude::*;
 # use std::convert::{TryInto, TryFrom};
 # #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u32", bytes = "2")]
+#[deku(type = "u32", bytes = 2)]
 enum DekuTest {
-    #[deku(id = "0xBEEF")]
+    #[deku(id = 0xBEEF)]
     VariantA(u8),
 }
 
@@ -1201,5 +1239,5 @@ let value: Vec<u8> = value.try_into().unwrap();
 assert_eq!(data, value);
 ```
 
-
+[reader.end()]: crate::reader::Reader::end()
 */
diff --git a/src/ctx.rs b/src/ctx.rs
index 3728328..5565be2 100644
--- a/src/ctx.rs
+++ b/src/ctx.rs
@@ -95,6 +95,9 @@ pub enum Limit<T, Predicate: FnMut(&T) -> bool> {
 
     /// Read until a given quantity of bits have been read
     BitSize(BitSize),
+
+    /// Read until `reader.end()` is true. Used for `read_all` attribute.
+    End,
 }
 
 impl<T> From<usize> for Limit<T, fn(&T) -> bool> {
@@ -135,6 +138,14 @@ impl<T, Predicate: for<'a> FnMut(&'a T) -> bool> Limit<T, Predicate> {
     }
 }
 
+impl<T> Limit<T, fn(&T) -> bool> {
+    /// Read until `reader.end()` is true
+    #[inline]
+    pub fn end() -> Self {
+        Self::End
+    }
+}
+
 impl<T> Limit<T, fn(&T) -> bool> {
     /// Constructs a new Limit that reads until the given number of elements are read
     #[inline]
diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs
index b51ea80..9d6e4fa 100644
--- a/src/impls/hashmap.rs
+++ b/src/impls/hashmap.rs
@@ -42,6 +42,30 @@ where
     Ok(res)
 }
 
+fn from_reader_with_ctx_hashmap_to_end<'a, K, V, S, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<HashMap<K, V, S>, DekuError>
+where
+    K: DekuReader<'a, Ctx> + Eq + Hash,
+    V: DekuReader<'a, Ctx>,
+    S: BuildHasher + Default,
+    Ctx: Copy,
+{
+    let mut res = HashMap::with_capacity_and_hasher(capacity.unwrap_or(0), S::default());
+
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <(K, V)>::from_reader_with_ctx(reader, ctx)?;
+        res.insert(val.0, val.1);
+    }
+
+    Ok(res)
+}
+
 impl<'a, K, V, S, Ctx, Predicate> DekuReader<'a, (Limit<(K, V), Predicate>, Ctx)>
     for HashMap<K, V, S>
 where
@@ -136,6 +160,9 @@ where
                     move |read_bits, _| read_bits == bit_size,
                 )
             }
+
+            // Read until `reader.end()` is true
+            Limit::End => from_reader_with_ctx_hashmap_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -218,6 +245,7 @@ mod tests {
         case::count_2([0x01, 0xAA, 0x02, 0xBB, 0xBB].as_ref(), Endian::Little, Some(8), 2.into(), fxhashmap!{0x01 => 0xAA, 0x02 => 0xBB}, bits![u8, Msb0;], &[0xbb]),
         case::until_null([0x01, 0xAA, 0, 0, 0xBB].as_ref(), Endian::Little, None, (|kv: &(u8, u8)| kv.0 == 0u8 && kv.1 == 0u8).into(), fxhashmap!{0x01 => 0xAA, 0 => 0}, bits![u8, Msb0;], &[0xbb]),
         case::until_bits([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(16).into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]),
+        case::read_all([0x01, 0xAA].as_ref(), Endian::Little, None, Limit::end(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[]),
         case::bits_6([0b0000_0100, 0b1111_0000, 0b1000_0000].as_ref(), Endian::Little, Some(6), 2.into(), fxhashmap!{0x01 => 0x0F, 0x02 => 0}, bits![u8, Msb0;], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashMap::default(), bits![u8, Msb0;], &[]),
diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs
index 16c4dea..fc2a69a 100644
--- a/src/impls/hashset.rs
+++ b/src/impls/hashset.rs
@@ -41,6 +41,29 @@ where
     Ok(res)
 }
 
+fn from_reader_with_ctx_hashset_to_end<'a, T, S, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<HashSet<T, S>, DekuError>
+where
+    T: DekuReader<'a, Ctx> + Eq + Hash,
+    S: BuildHasher + Default,
+    Ctx: Copy,
+{
+    let mut res = HashSet::with_capacity_and_hasher(capacity.unwrap_or(0), S::default());
+
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <T>::from_reader_with_ctx(reader, ctx)?;
+        res.insert(val);
+    }
+
+    Ok(res)
+}
+
 impl<'a, T, S, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for HashSet<T, S>
 where
     T: DekuReader<'a, Ctx> + Eq + Hash,
@@ -131,6 +154,9 @@ where
                     move |read_bits, _| read_bits == bit_size,
                 )
             }
+
+            // Read until `reader.end()` is true
+            Limit::End => from_reader_with_ctx_hashset_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -190,6 +216,7 @@ mod tests {
         case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[0xcc]),
         case::until_null([0xAA, 0, 0xBB].as_ref(), Endian::Little, None, (|v: &u8| *v == 0u8).into(), vec![0xAA, 0].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
         case::until_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(8).into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]),
+        case::read_all([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::end(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[]),
         case::bits_6([0b0110_1001, 0b1110_1001].as_ref(), Endian::Little, Some(6), 2.into(), vec![0b00_011010, 0b00_011110].into_iter().collect(), bits![u8, Msb0; 1, 0, 0, 1], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashSet::default(), bits![u8, Msb0;], &[]),
diff --git a/src/impls/vec.rs b/src/impls/vec.rs
index c19abea..3d17976 100644
--- a/src/impls/vec.rs
+++ b/src/impls/vec.rs
@@ -44,6 +44,27 @@ where
     Ok(res)
 }
 
+fn reader_vec_to_end<'a, T, Ctx, R: Read>(
+    reader: &mut crate::reader::Reader<R>,
+    capacity: Option<usize>,
+    ctx: Ctx,
+) -> Result<Vec<T>, DekuError>
+where
+    T: DekuReader<'a, Ctx>,
+    Ctx: Copy,
+{
+    let mut res = capacity.map_or_else(Vec::new, Vec::with_capacity);
+    loop {
+        if reader.end() {
+            break;
+        }
+        let val = <T>::from_reader_with_ctx(reader, ctx)?;
+        res.push(val);
+    }
+
+    Ok(res)
+}
+
 impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for Vec<T>
 where
     T: DekuReader<'a, Ctx>,
@@ -104,6 +125,8 @@ where
                     read_bits == bit_size
                 })
             }
+
+            Limit::End => reader_vec_to_end(reader, None, inner_ctx),
         }
     }
 }
@@ -157,6 +180,8 @@ mod tests {
         case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB], bits![u8, Msb0;], &[0xcc]),
         case::until_null([0xAA, 0, 0xBB].as_ref(), Endian::Little, None, (|v: &u8| *v == 0u8).into(), vec![0xAA, 0], bits![u8, Msb0;], &[0xbb]),
         case::until_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(8).into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]),
+        case::end([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::end(), vec![0xaa, 0xbb], bits![u8, Msb0;], &[]),
+        case::end_bitsize([0xf0, 0xf0].as_ref(), Endian::Little, Some(4), Limit::end(), vec![0xf, 0x0, 0x0f, 0x0], bits![u8, Msb0;], &[]),
         case::bits_6([0b0110_1001, 0b1110_1001].as_ref(), Endian::Little, Some(6), 2.into(), vec![0b00_011010, 0b00_011110], bits![u8, Msb0; 1, 0, 0, 1], &[]),
         #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")]
         case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), vec![], bits![u8, Msb0;], &[]),
diff --git a/src/lib.rs b/src/lib.rs
index c3d8c8d..64d36de 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,9 +38,9 @@ use deku::prelude::*;
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 #[deku(endian = "big")]
 struct DekuTest {
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     field_a: u8,
-    #[deku(bits = "4")]
+    #[deku(bits = 4)]
     field_b: u8,
     field_c: u16,
 }
@@ -176,9 +176,9 @@ use deku::prelude::*;
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
 #[deku(type = "u8")]
 enum DekuTest {
-    #[deku(id = "0x01")]
+    #[deku(id = 0x01)]
     VariantA,
-    #[deku(id = "0x02")]
+    #[deku(id = 0x02)]
     VariantB(u16),
 }
 
diff --git a/src/reader.rs b/src/reader.rs
index 6de4b35..cf55193 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -55,9 +55,9 @@ impl<'a, R: Read> Reader<'a, R> {
     /// #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
     /// #[deku(endian = "big")]
     /// struct DekuTest {
-    ///     #[deku(bits = "4")]
+    ///     #[deku(bits = 4)]
     ///     field_a: u8,
-    ///     #[deku(bits = "2")]
+    ///     #[deku(bits = 2)]
     ///     field_b: u8,
     /// }
     /// //                       |         | <= this entire byte is Read
@@ -78,7 +78,8 @@ impl<'a, R: Read> Reader<'a, R> {
         self.leftover.iter().by_vals().collect()
     }
 
-    /// Return true if we are at the end of a reader and there are no cached bits in the reader
+    /// Return true if we are at the end of a reader and there are no cached bits in the reader.
+    /// Since this uses [Read] internally, this will return true when [Read] returns [ErrorKind::UnexpectedEof].
     ///
     /// The byte that was read will be internally buffered
     #[inline]
@@ -106,7 +107,7 @@ impl<'a, R: Read> Reader<'a, R> {
     }
 
     /// Used at the beginning of `from_reader`.
-    /// TODO: maybe send into read_bytes() if amt >= 8
+    // TODO: maybe send into read_bytes() if amt >= 8
     #[inline]
     pub fn skip_bits(&mut self, amt: usize) -> Result<(), DekuError> {
         #[cfg(feature = "logging")]


Test patch:
diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs
index 5a735d1..32dd937 100644
--- a/tests/test_alloc.rs
+++ b/tests/test_alloc.rs
@@ -21,7 +21,7 @@ enum NestedEnum {
 }
 
 #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-#[deku(type = "u32", bytes = "2", ctx = "_endian: Endian")]
+#[deku(type = "u32", bytes = 2, ctx = "_endian: Endian")]
 enum NestedEnum2 {
     #[deku(id = "0x01")]
     VarA(u8),
@@ -37,9 +37,9 @@ struct TestDeku {
     #[deku(count = "1")]
     field_e: Vec<u8>, // 1 alloc
     field_f: [u8; 3],
-    #[deku(bits = "3")]
+    #[deku(bits = 3)]
     field_g: u8, // 3 allocs (read_bits(Ordering::Greater))
-    #[deku(bits = "5")]
+    #[deku(bits = 5)]
     field_h: u8, // 1 alloc (read_bits(Ordering::Equal))
                  //field_i: NestedEnum2,
 }
diff --git a/tests/test_attributes/test_ctx.rs b/tests/test_attributes/test_ctx.rs
index 7795ff6..3d16fcd 100644
--- a/tests/test_attributes/test_ctx.rs
+++ b/tests/test_attributes/test_ctx.rs
@@ -136,7 +136,7 @@ fn test_struct_enum_ctx_id() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
     struct StructEnumId {
         my_id: u8,
-        #[deku(bytes = "1")]
+        #[deku(bytes = 1)]
         data: usize,
         #[deku(ctx = "*my_id, *data")]
         enum_from_id: EnumId,
diff --git a/tests/test_attributes/test_limits/mod.rs b/tests/test_attributes/test_limits/mod.rs
index 48aa212..c7e74d0 100644
--- a/tests/test_attributes/test_limits/mod.rs
+++ b/tests/test_attributes/test_limits/mod.rs
@@ -1,4 +1,5 @@
 mod test_bits_read;
 mod test_bytes_read;
 mod test_count;
+mod test_read_all;
 mod test_until;
diff --git a/tests/test_attributes/test_limits/test_read_all.rs b/tests/test_attributes/test_limits/test_read_all.rs
new file mode 100644
index 0000000..4e91388
--- /dev/null
+++ b/tests/test_attributes/test_limits/test_read_all.rs
@@ -0,0 +1,46 @@
+use std::convert::{TryFrom, TryInto};
+
+use deku::prelude::*;
+
+#[test]
+fn test_read_all() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct1 {
+        #[deku(read_all)]
+        data: Vec<u8>,
+    }
+
+    let test_data: Vec<u8> = [0xaa, 0xbb].to_vec();
+
+    let ret_read = TestStruct1::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct1 {
+            data: test_data.to_vec()
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    struct TestStruct2 {
+        first: u8,
+        #[deku(read_all)]
+        data: Vec<u8>,
+    }
+
+    let test_data: Vec<u8> = [0xff, 0xaa, 0xbb].to_vec();
+
+    let ret_read = TestStruct2::try_from(test_data.as_slice()).unwrap();
+    assert_eq!(
+        TestStruct2 {
+            first: 0xff,
+            data: test_data[1..].to_vec()
+        },
+        ret_read
+    );
+
+    let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+    assert_eq!(test_data, ret_write);
+}
diff --git a/tests/test_compile/cases/bits_bytes_conflict.rs b/tests/test_compile/cases/bits_bytes_conflict.rs
index 20ab2a8..bc43d3b 100644
--- a/tests/test_compile/cases/bits_bytes_conflict.rs
+++ b/tests/test_compile/cases/bits_bytes_conflict.rs
@@ -1,26 +1,26 @@
 use deku::prelude::*;
 
 #[derive(DekuRead)]
-#[deku(type = "u8", bits = "1", bytes = "2")]
+#[deku(type = "u8", bits = 1, bytes = 2)]
 enum Test1 {}
 
 #[derive(DekuRead)]
 #[deku(type = "u8")]
 enum Test2 {
-    A(#[deku(bits = "1", bytes = "2")] u8),
+    A(#[deku(bits = 1, bytes = 2)] u8),
     B {
-        #[deku(bits = "3", bytes = "4")]
+        #[deku(bits = 3, bytes = 4)]
         a: u8,
     },
 }
 
 #[derive(DekuRead)]
 struct Test3 {
-    #[deku(bits = "5", bytes = "6")]
+    #[deku(bits = 5, bytes = 6)]
     a: u8,
 }
 
 #[derive(DekuRead)]
-struct Test4(#[deku(bits = "7", bytes = "8")] u8);
+struct Test4(#[deku(bits = 7, bytes = 8)] u8);
 
 fn main() {}
diff --git a/tests/test_compile/cases/bits_bytes_conflict.stderr b/tests/test_compile/cases/bits_bytes_conflict.stderr
index d8c433e..ff8f9d5 100644
--- a/tests/test_compile/cases/bits_bytes_conflict.stderr
+++ b/tests/test_compile/cases/bits_bytes_conflict.stderr
@@ -1,23 +1,23 @@
 error: conflicting: both `bits` and `bytes` specified on enum
  --> $DIR/bits_bytes_conflict.rs:4:28
   |
-4 | #[deku(type = "u8", bits = "1", bytes = "2")]
-  |                            ^^^
+4 | #[deku(type = "u8", bits = 1, bytes = 2)]
+  |                            ^
 
 error: conflicting: both `bits` and `bytes` specified on field
   --> $DIR/bits_bytes_conflict.rs:10:21
    |
-10 |     A(#[deku(bits = "1", bytes = "2")] u8),
-   |                     ^^^
+10 |     A(#[deku(bits = 1, bytes = 2)] u8),
+   |                     ^
 
 error: conflicting: both `bits` and `bytes` specified on field
   --> $DIR/bits_bytes_conflict.rs:19:19
    |
-19 |     #[deku(bits = "5", bytes = "6")]
-   |                   ^^^
+19 |     #[deku(bits = 5, bytes = 6)]
+   |                   ^
 
 error: conflicting: both `bits` and `bytes` specified on field
   --> $DIR/bits_bytes_conflict.rs:24:28
    |
-24 | struct Test4(#[deku(bits = "7", bytes = "8")] u8);
-   |                            ^^^
+24 | struct Test4(#[deku(bits = 7, bytes = 8)] u8);
+   |                            ^
diff --git a/tests/test_compile/cases/count_read_conflict.rs b/tests/test_compile/cases/count_read_conflict.rs
index 7054b6d..a9cfe62 100644
--- a/tests/test_compile/cases/count_read_conflict.rs
+++ b/tests/test_compile/cases/count_read_conflict.rs
@@ -24,5 +24,22 @@ struct Test4 {
     a: Vec<u8>,
 }
 
+#[derive(DekuRead)]
+struct Test5 {
+    #[deku(count = "1", read_all)]
+    a: Vec<u8>,
+}
+
+#[derive(DekuRead)]
+struct Test6 {
+    #[deku(bytes_read = "1", read_all)]
+    a: Vec<u8>,
+}
+
+#[derive(DekuRead)]
+struct Test7 {
+    #[deku(bits_read = "1", read_all)]
+    a: Vec<u8>,
+}
 
 fn main() {}
diff --git a/tests/test_compile/cases/count_read_conflict.stderr b/tests/test_compile/cases/count_read_conflict.stderr
index 92e9aa9..19f29cf 100644
--- a/tests/test_compile/cases/count_read_conflict.stderr
+++ b/tests/test_compile/cases/count_read_conflict.stderr
@@ -21,3 +21,27 @@ error: conflicting: both `count` and `bytes_read` specified on field
    |
 23 |     #[deku(count = "1", bytes_read = "2")]
    |                    ^^^
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:27:10
+   |
+27 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:33:10
+   |
+33 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: conflicting: `read_all` cannot be used with `until`, count`, `bits_read`, or `bytes_read`
+  --> $DIR/count_read_conflict.rs:39:10
+   |
+39 | #[derive(DekuRead)]
+   |          ^^^^^^^^
+   |
+   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/test_compile/cases/enum_validation.rs b/tests/test_compile/cases/enum_validation.rs
index 9cd2008..cf1aedc 100644
--- a/tests/test_compile/cases/enum_validation.rs
+++ b/tests/test_compile/cases/enum_validation.rs
@@ -13,49 +13,50 @@ enum Test2 {}
 #[derive(DekuRead)]
 #[deku(type = "u8")]
 enum Test3 {
-    #[deku(id = "1", id_pat = "2..=3")] A(u8),
+    #[deku(id = "1", id_pat = "2..=3")]
+    A(u8),
 }
 
 // test `type` only allowed on enum
 #[derive(DekuRead)]
 #[deku(type = "u8")]
 struct Test4 {
-    a: u8
+    a: u8,
 }
 
 // test `bits` only allowed on enum
 #[derive(DekuRead)]
-#[deku(bits = "1")]
+#[deku(bits = 1)]
 struct Test5 {
-    a: u8
+    a: u8,
 }
 
 // test `bytes` only allowed on enum
 #[derive(DekuRead)]
-#[deku(bits = "1")]
+#[deku(bits = 1)]
 struct Test6 {
-    a: u8
+    a: u8,
 }
 
 // test `id` only allowed on enum
 #[derive(DekuRead)]
 #[deku(id = "test")]
 struct Test7 {
-    a: u8
+    a: u8,
 }
 
 // test `bits` cannot be used with `id`
 #[derive(DekuRead)]
-#[deku(id = "test", bits = "4")]
+#[deku(id = "test", bits = 4)]
 enum Test8 {
-    A
+    A,
 }
 
 // test `bytes` cannot be used with `id`
 #[derive(DekuRead)]
-#[deku(id = "test", bytes = "4")]
+#[deku(id = "test", bytes = 4)]
 enum Test9 {
-    A
+    A,
 }
 
 // test `id` cannot be `_`
@@ -63,7 +64,7 @@ enum Test9 {
 #[deku(type = "u8")]
 enum Test10 {
     #[deku(id = "_")]
-    A
+    A,
 }
 
 // test missing `id`
@@ -72,7 +73,7 @@ enum Test10 {
 enum Test11 {
     #[deku(id = "1")]
     A,
-    B(u8)
+    B(u8),
 }
 
 fn main() {}
diff --git a/tests/test_compile/cases/enum_validation.stderr b/tests/test_compile/cases/enum_validation.stderr
index e097cb3..1ebfc69 100644
--- a/tests/test_compile/cases/enum_validation.stderr
+++ b/tests/test_compile/cases/enum_validation.stderr
@@ -13,53 +13,53 @@ error: conflicting: both `type` and `id` specified on enum
 error: conflicting: both `id` and `id_pat` specified on variant
   --> $DIR/enum_validation.rs:16:17
    |
-16 |     #[deku(id = "1", id_pat = "2..=3")] A(u8),
+16 |     #[deku(id = "1", id_pat = "2..=3")]
    |                 ^^^
 
 error: `type` only supported on enum
-  --> $DIR/enum_validation.rs:21:15
+  --> $DIR/enum_validation.rs:22:15
    |
-21 | #[deku(type = "u8")]
+22 | #[deku(type = "u8")]
    |               ^^^^
 
 error: `bits` only supported on enum
-  --> $DIR/enum_validation.rs:28:15
+  --> $DIR/enum_validation.rs:29:15
    |
-28 | #[deku(bits = "1")]
-   |               ^^^
+29 | #[deku(bits = 1)]
+   |               ^
 
 error: `bits` only supported on enum
-  --> $DIR/enum_validation.rs:35:15
+  --> $DIR/enum_validation.rs:36:15
    |
-35 | #[deku(bits = "1")]
-   |               ^^^
+36 | #[deku(bits = 1)]
+   |               ^
 
 error: `id` only supported on enum
-  --> $DIR/enum_validation.rs:42:13
+  --> $DIR/enum_validation.rs:43:13
    |
-42 | #[deku(id = "test")]
+43 | #[deku(id = "test")]
    |             ^^^^^^
 
 error: error: cannot use `bits` with `id`
-  --> $DIR/enum_validation.rs:50:6
+  --> $DIR/enum_validation.rs:51:6
    |
-50 | enum Test8 {
+51 | enum Test8 {
    |      ^^^^^
 
 error: error: cannot use `bytes` with `id`
-  --> $DIR/enum_validation.rs:57:6
+  --> $DIR/enum_validation.rs:58:6
    |
-57 | enum Test9 {
+58 | enum Test9 {
    |      ^^^^^
 
 error: error: `id_pat` should be used for `_`
-  --> $DIR/enum_validation.rs:66:5
+  --> $DIR/enum_validation.rs:67:5
    |
-66 |     A
+67 |     A,
    |     ^
 
 error: DekuRead: `id` must be specified on non-unit variants
-  --> $DIR/enum_validation.rs:75:5
+  --> $DIR/enum_validation.rs:76:5
    |
-75 |     B(u8)
+76 |     B(u8),
    |     ^
diff --git a/tests/test_compile/cases/temp_field.stderr b/tests/test_compile/cases/temp_field.stderr
index f19994d..86b0716 100644
--- a/tests/test_compile/cases/temp_field.stderr
+++ b/tests/test_compile/cases/temp_field.stderr
@@ -6,18 +6,14 @@ error[E0063]: missing field `field_a` in initializer of `Test1`
   |
   = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0027]: pattern does not mention field `field_a`
+error: pattern requires `..` due to inaccessible fields
  --> $DIR/temp_field.rs:4:20
   |
 4 | #[derive(DekuRead, DekuWrite)]
-  |                    ^^^^^^^^^ missing field `field_a`
+  |                    ^^^^^^^^^
   |
   = note: this error originates in the derive macro `DekuWrite` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: include the missing field in the pattern
+help: ignore the inaccessible and unused fields
   |
-4 | #[derive(DekuRead, DekuWrite { field_a })]
-  |                              +++++++++++
-help: if you don't care about this missing field, you can explicitly ignore it
-  |
-4 | #[derive(DekuRead, DekuWrite { .. })]
-  |                              ++++++
+4 | #[derive(DekuRead, DekuWrite { .. } {
+  |                              ~~~~~~
diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs
index dd14d58..b40e529 100644
--- a/tests/test_from_bytes.rs
+++ b/tests/test_from_bytes.rs
@@ -31,12 +31,12 @@ fn test_from_bytes_struct() {
 #[test]
 fn test_from_bytes_enum() {
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = "4")]
+    #[deku(type = "u8", bits = 4)]
     enum TestDeku {
         #[deku(id = "0b0110")]
-        VariantA(#[deku(bits = "4")] u8),
+        VariantA(#[deku(bits = 4)] u8),
         #[deku(id = "0b0101")]
-        VariantB(#[deku(bits = "2")] u8),
+        VariantB(#[deku(bits = 2)] u8),
     }
 
     let test_data: Vec<u8> = [0b0110_0110u8, 0b0101_1010u8].to_vec();
diff --git a/tests/test_from_reader.rs b/tests/test_from_reader.rs
index 537a6b9..eb7780f 100644
--- a/tests/test_from_reader.rs
+++ b/tests/test_from_reader.rs
@@ -37,12 +37,12 @@ fn test_from_reader_struct() {
 #[test]
 fn test_from_reader_enum() {
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = "4")]
+    #[deku(type = "u8", bits = 4)]
     enum TestDeku {
         #[deku(id = "0b0110")]
-        VariantA(#[deku(bits = "4")] u8),
+        VariantA(#[deku(bits = 4)] u8),
         #[deku(id = "0b0101")]
-        VariantB(#[deku(bits = "2")] u8),
+        VariantB(#[deku(bits = 2)] u8),
     }
 
     let test_data = [0b0110_0110u8, 0b0101_1010u8];
diff --git a/tests/test_regression.rs b/tests/test_regression.rs
index 28fbcb8..84ff990 100644
--- a/tests/test_regression.rs
+++ b/tests/test_regression.rs
@@ -18,7 +18,7 @@ fn issue_224() {
     }
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = "2")]
+    #[deku(type = "u8", bits = 2)]
     pub enum One {
         Start = 0,
         Go = 1,
@@ -26,7 +26,7 @@ fn issue_224() {
     }
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
-    #[deku(type = "u8", bits = "4")]
+    #[deku(type = "u8", bits = 4)]
     pub enum Two {
         #[deku(id = "0b0000")]
         Put(Op1),
@@ -40,17 +40,17 @@ fn issue_224() {
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
     pub struct Op1 {
-        #[deku(bits = "2")]
+        #[deku(bits = 2)]
         pub i: u8,
-        #[deku(bits = "4")]
+        #[deku(bits = 4)]
         pub o: u8,
     }
 
     #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
     pub struct Op2 {
-        #[deku(bits = "8")]
+        #[deku(bits = 8)]
         pub w: u8,
-        #[deku(bits = "6")]
+        #[deku(bits = 6)]
         pub j: u8,
     }
 
@@ -359,3 +359,35 @@ fn issue_310() {
     #[derive(DekuRead, DekuWrite)]
     struct Test {}
 }
+
+#[test]
+fn issue_397() {
+    use deku::prelude::*;
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    struct Header {
+        kind: PacketType,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    #[deku(type = "u8", endian = "big")]
+    enum PacketType {
+        #[deku(id = 0)]
+        Zero,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    struct Packet {
+        header: Header,
+        #[deku(ctx = "header")]
+        payload: Payload,
+    }
+
+    #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)]
+    #[deku(ctx = "header: &Header", id = "header.kind")]
+    enum Payload {
+        #[deku(id = "PacketType::Zero")]
+        Zero(u8),
+    }
+    let _ = Packet::from_bytes((&[0x00, 0x01], 0));
+}
diff --git a/tests/test_struct.rs b/tests/test_struct.rs
index 063bf5a..9880309 100644
--- a/tests/test_struct.rs
+++ b/tests/test_struct.rs
@@ -18,9 +18,9 @@ pub struct DoubleNestedDeku {
 // Common struct to test nesting
 #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
 pub struct NestedDeku {
-    #[deku(bits = "6")]
+    #[deku(bits = 6)]
     pub nest_a: u8,
-    #[deku(bits = "2")]
+    #[deku(bits = 2)]
     pub nest_b: u8,
 
     pub inner: DoubleNestedDeku,
@@ -31,7 +31,7 @@ pub struct NestedDeku {
 fn test_read_too_much_data() {
     #[derive(DekuRead)]
     pub struct TestStruct {
-        #[deku(bits = "6")]
+        #[deku(bits = 6)]
         pub field_a: u8,
     }
 
@@ -44,9 +44,9 @@ fn test_unnamed_struct() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
     pub struct TestUnamedStruct(
         pub u8,
-        #[deku(bits = "2")] pub u8,
-        #[deku(bits = "6")] pub u8,
-        #[deku(bytes = "2")] pub u16,
+        #[deku(bits = 2)] pub u8,
+        #[deku(bits = 6)] pub u8,
+        #[deku(bytes = 2)] pub u16,
         #[deku(endian = "big")] pub u16,
         pub NestedDeku,
         #[deku(update = "self.7.len()")] pub u8,
@@ -101,11 +101,11 @@ fn test_named_struct() {
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
     pub struct TestStruct {
         pub field_a: u8,
-        #[deku(bits = "2")]
+        #[deku(bits = 2)]
         pub field_b: u8,
-        #[deku(bits = "6")]
+        #[deku(bits = 6)]
         pub field_c: u8,
-        #[deku(bytes = "2")]
+        #[deku(bytes = 2)]
         pub field_d: u16,
         #[deku(update = "1+self.field_d")]
         #[deku(endian = "big")]