[][src]Module deku::attributes

A documentation-only module for #[deku] attributes

List of attributes

AttributeScopeDescription
endiantop-level, fieldSet the endianness
bitsfieldSet the bit-size of the field
bytesfieldSet the byte-size of the field
countfieldSet the field representing the element count of a container
updatefieldApply code over the field when .update() is called
skipfieldSkip the reading/writing of a field
condfieldConditional expression for the field
defaultfieldCustom defaulting code when skip is true
mapfieldApply a function over the result of reading
readervariant, fieldCustom reader code
writervariant, fieldCustom writer code
enum: idtop-level, variantenum or variant id value
enum: id_patvariantvariant id match pattern
enum: id_typetop-levelSet the type of the variant id
enum: id_bitstop-levelSet the bit-size of the variant id
enum: id_bytestop-levelSet the byte-size of the variant id
ctxtop-levelContext argument list for context sensitive parsing
ctxfieldContext arguments to pass to field

endian

Set to read/write bytes in a specific byte order.

Values: big or little

Precedence: field > top-level > system endianness (default)

Example:

#[deku(endian = "little")] // top-level, defaults to system endianness
struct DekuTest {
    #[deku(endian = "big")] // field-level override
    field_be: u16,
    field_default: u16, // defaults to top-level
}

let data: Vec<u8> = vec![0xAB, 0xCD, 0xAB, 0xCD];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest {
       field_be: 0xABCD,
       field_default: 0xCDAB,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

bits

Set the bit-size of the field

Note: Cannot be used in combination with bytes

Example:

struct DekuTest {
    #[deku(bits = 2)]
    field_a: u8,
    #[deku(bits = 6)]
    field_b: u8,
    field_c: u8, // defaults to size_of<u8>*8
}

let data: Vec<u8> = vec![0b11_101010, 0xFF];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest {
       field_a: 0b11,
       field_b: 0b101010,
       field_c: 0xFF,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

bytes

Set the byte-size of the field

Note: Cannot be used in combination with bits

Example:

struct DekuTest {
    #[deku(bytes = 2)]
    field_a: u32,
    field_b: u8, // defaults to size_of<u8>
}

let data: Vec<u8> = vec![0xAB, 0xCD, 0xFF];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest {
       field_a: 0xCDAB,
       field_b: 0xFF,
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

count

Specify the field representing the length of the container, i.e. a Vec

Example:

struct DekuTest {
    #[deku(update = "self.items.len()")]
    count: u8,
    #[deku(count = "count")]
    items: Vec<u8>,
}

let data: Vec<u8> = vec![0x02, 0xAB, 0xCD];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest {
       count: 0x02,
       items: vec![0xAB, 0xCD],
    },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

Note: See update for more information on the attribute!

update

Specify custom code to run on the field when .update() is called on the struct/enum

Example:

use deku::prelude::*;
use std::convert::{TryInto, TryFrom};
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
    #[deku(update = "self.items.len()")]
    count: u8,
    #[deku(count = "count")]
    items: Vec<u8>,
}

let data: Vec<u8> = vec![0x02, 0xAB, 0xCD];

let mut value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { count: 0x02, items: vec![0xAB, 0xCD] },
    value
);

value.items.push(0xFF); // new item!
value.update().unwrap();

assert_eq!(
    DekuTest { count: 0x03, items: vec![0xAB, 0xCD, 0xFF] },
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x03, 0xAB, 0xCD, 0xFF], value);

skip

Skip the reading/writing of a field.

Defaults value to default

Note: Can be paired with cond to have conditional skipping

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    pub field_a: u8,
    #[deku(skip)]
    pub field_b: Option<u8>,
    pub field_c: u8,
}

let data: Vec<u8> = vec![0x01, 0x02];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: None, field_c: 0x02 },
    value
);

cond

Specify a condition to parse or skip a field

Note: Can be paired with default

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    field_a: u8,
    #[deku(cond = "*field_a == 0x01")]
    field_b: Option<u8>,
    #[deku(cond = "*field_b == Some(0xFF)", default = "Some(0x05)")]
    field_c: Option<u8>,
    #[deku(skip, cond = "*field_a == 0x01", default = "Some(0x06)")]
    field_d: Option<u8>,
}

let data: Vec<u8> = vec![0x01, 0x02];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: Some(0x02), field_c: Some(0x05), field_d: Some(0x06)},
    value
);

assert_eq!(
    vec![0x01, 0x02, 0x05],
    value.to_bytes().unwrap(),
)

default

Default code tokens used with skip or cond

Defaults to Default::default()

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    pub field_a: u8,
    #[deku(skip, default = "Some(*field_a)")]
    pub field_b: Option<u8>,
    pub field_c: u8,
}

let data: Vec<u8> = vec![0x01, 0x02];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { field_a: 0x01, field_b: Some(0x01), field_c: 0x02 },
    value
);

map

Specify a function or lambda to apply to the result of the read

Example:

Read a u8 and apply a function to convert it to a String.

#[derive(PartialEq, Debug, DekuRead)]
pub struct DekuTest {
    #[deku(map = "|field: u8| -> Result<_, DekuError> { Ok(field.to_string()) }")]
    pub field_a: String,
    #[deku(map = "DekuTest::map_field_b")]
    pub field_b: String,
}

impl DekuTest {
    fn map_field_b(field_b: u8) -> Result<String, DekuError> {
        Ok(field_b.to_string())
    }
}

let data: Vec<u8> = vec![0x01, 0x02];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { field_a: "1".to_string(), field_b: "2".to_string() },
    value
);

reader/writer

Specify custom reader or writer tokens for reading a field or variant

Example:

struct DekuTest {
    #[deku(
        reader = "DekuTest::read(rest)",
        writer = "DekuTest::write(&self.field_a)"
    )]
    field_a: String,
}

impl DekuTest {
    // Read and convert to String
    fn read(
        rest: &BitSlice<Msb0, u8>,
    ) -> Result<(&BitSlice<Msb0, u8>, String), DekuError> {
        let (rest, value) = u8::read(rest, ())?;
        Ok((rest, value.to_string()))
    }

    // Parse from String to u8 and write
    fn write(field_a: &str) -> Result<BitVec<Msb0, u8>, DekuError> {
        let value = field_a.parse::<u8>().unwrap();
        value.write(())
    }
}

let data: Vec<u8> = vec![0x01];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest { field_a: "1".to_string() },
    value
);


let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

id

id (top-level)

Specify the enum id

This is useful in cases when the enum id is already consumed or is given externally

Example:

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
pub struct DekuTest {
    pub my_id: u8,
    pub data: u8,
    #[deku(ctx = "*my_id")]
    pub enum_from_id: MyEnum,
}

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(ctx = "my_id: u8", id = "my_id")]
pub enum MyEnum {
    #[deku(id = "1")]
    VariantA(u8),
    #[deku(id = "2")]
    VariantB,
}

let data: Vec<u8> = vec![0x01_u8, 0xff, 0xab];
let ret_read = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest {
        my_id: 0x01,
        data: 0xff,
        enum_from_id: MyEnum::VariantA(0xab),
    },
    ret_read
);

let ret_write: Vec<u8> = ret_read.try_into().unwrap();
assert_eq!(ret_write, data)

id (variant)

Specify the identifier of the enum variant, must be paired with id_type or id (top-level)

Note: If no id is specified, the variant is treated as the "catch-all".

Example:

#[deku(id_type = "u8")]
enum DekuTest {
    #[deku(id = "0x01")]
    VariantA(u8),
    #[deku(id = "0x02")]
    VariantB(u8, u16),

    VariantCatchAll { // Catch-all variant
        type_: u8
    },
}

let data: Vec<u8> = vec![0x01, 0xFF, 0x02, 0xAB, 0xEF, 0xBE, 0xFF];

let (rest, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();

assert_eq!(
    DekuTest::VariantA(0xFF),
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x01, 0xFF], variant_bytes);

let (rest, value) = DekuTest::from_bytes(rest).unwrap();

assert_eq!(
    DekuTest::VariantB(0xAB, 0xBEEF),
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x02, 0xAB, 0xEF, 0xBE], variant_bytes);

let (rest, value) = DekuTest::from_bytes(rest).unwrap();

assert_eq!(
    DekuTest::VariantCatchAll { type_: 0xFF },
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0xFF], variant_bytes);

id_pat

Specify the identifier in the form of a match pattern for the enum variant.

The enum variant must have space to store the identifier for proper writing.

Example:

#[deku(id_type = "u8")]
enum DekuTest {
    #[deku(id = "0x01")]
    VariantA(u8),
    #[deku(id_pat = "0x02..=0x06")]
    VariantB {
        id: u8
    },
}

let data: Vec<u8> = vec![0x02];

let (rest, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();

assert_eq!(
    DekuTest::VariantB { id: 0x02 },
    value
);

let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, variant_bytes);

id_type

Specify the type of the enum variant id to consume, see example

id_bits

Set the bit size of the enum variant id

Note: Cannot be used in combination with id_bytes

Example:

#[deku(id_type = "u8", id_bits = "4")]
enum DekuTest {
    #[deku(id = "0b1001")]
    VariantA( #[deku(bits = "4")] u8, u8),
}

let data: Vec<u8> = vec![0b1001_0110, 0xFF];

let (rest, value) = DekuTest::from_bytes((&data, 0)).unwrap();

assert_eq!(
    DekuTest::VariantA(0b0110, 0xFF),
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

id_bytes

Set the byte size of the enum variant id

Note: Cannot be used in combination with id_bits

Example:

#[deku(id_type = "u32", id_bytes = "2")]
enum DekuTest {
    #[deku(id = "0xBEEF")]
    VariantA(u8),
}

let data: Vec<u8> = vec![0xEF, 0xBE, 0xFF];

let value = DekuTest::try_from(data.as_ref()).unwrap();

assert_eq!(
    DekuTest::VariantA(0xFF),
    value
);

let value: Vec<u8> = value.try_into().unwrap();
assert_eq!(data, value);

top_level_ctx

Argument list of external context required for parsing/writing.

Value: The value of a ctx attribute must be a literal string which can be parsed to function argument list:

This example is not tested
#[deku(ctx = "a: u8, b: String")] // <-- valid
struct A{}
#[deku(ctx = "10, true")] // <-- invalid
struct B{}

Example:

#[derive(DekuRead, DekuWrite)]
#[deku(ctx = "_a: u8")]
struct Test {}

let data: Vec<u8> = vec![0xEF, 0xBE, 0xFF];

let (_, value) = Test::read(data.bits(), 10).unwrap();
// let _ = Test::read(data.bits(), ""); <-- compile error.
let _ = value.write(10);
// let _ = Test::write(true); <-- compile error.

field_level_ctx

Pass context arguments to a type which accepts them.

Value: This attribute accepts a literal string which can be parsed to expression list:

This example is not tested
struct Test {
    p: u8,
    #[deku(ctx = r#"p, 12, "str", true"#)] // <-- valid
    a: u8,
    #[deku(ctx = ",")] // <-- invalid
    b: u8,
}

Visibility: List of what you can access:

  1. All former fields which have been parsed (you can only access const reference, no move, no mut).
  2. Every context defined through top-level-ctx attribute.

Example

#[derive(DekuRead, DekuWrite)]
#[deku(ctx = "a: u8")]
struct Subtype {
    #[deku(map = "|b: u8| -> Result<_, DekuError> { Ok(b + a) }")]
    b: u8
}

#[derive(DekuRead, DekuWrite)]
struct Test {
    a: u8,
    #[deku(ctx = "*a")] // `a` is a reference
    sub: Subtype
}

let data: Vec<u8> = vec![0x01, 0x02];

let (rest, value) = Test::from_bytes((&data[..], 0)).unwrap();
assert_eq!(value.a, 0x01);
assert_eq!(value.sub.b, 0x01 + 0x02)

In addition, currently, endian, bytes and bits are a sugar of ctx, examples below are equivalent:

This example is not tested
struct Type1 {
    #[deku(endian = "big", bits = "1")]
    field: u8,
}

struct Type2 {
    #[deku(ctx = "Endian::Big, BitSize(1)")]
    field: u8,
}