thalassa-derive 0.0.4

`thalassa` derive macros
Documentation

thalassa-derive

Crates.io docs.rs

Description

Derives for thalassa

Note: A lot of this is similar to tls_codec's derives, in an effort to ease migration between the derives/attrs

Attributes

On Enums

  • #[tlspl(untagged)] - Marks that this enum contents should not be de/serialized preceded by their discriminant. This is especially useful when using the #[tlspl(select)] attribute
  • #[tlspl(extensible)] - This marks an enum as "extensible", meaning that the contents of the variants (eg. its fields) will be serialized as a variable-length bytes container, to ensure that it can be extended, serialized and deserialized even in the case of future evolutions or downstream-defined extensions. This is inspired by how MLS (RFC9420) Extensions are done.

On Enum Variants

  • #[tlspl(discrmininant = "path::to::constant"|1337)] - Allows to point to a discriminant tag for cases where it's not supported by the Rust language (eg complex data types/structs where explicit discriminant isn't allowed).
    • This also supports the following forms
      • integer literals: #[tlspl(discriminant = 69)]
      • constants: #[tlspl(discriminant = "MY_CONST")] - note: wrapped in a string
      • enum variants that have an explicit discriminant: #[tlspl(discriminant = "MyEnum::CaseN")] - note: wrapped in a string
  • #[tlspl(other)] - Marks a "catch-all" or "unknown" variant that needs to be a tuple having 1 field equal to the #[repr] of the enum (for naked enums), or 2 fields equal to (repr, Cow<[u8]>) (for enums that have data)
    • Example
#[derive(TlsplSize, TlsplDeserialize, TlsplSerialize)]
#[repr(u8)]
enum Thing {
    CaseA = 0,
    CaseB = 1,
    #[tlspl(other)]
    Unknown(u8)
}

#[derive(TlsplSize, TlsplDeserialize, TlsplSerialize)]
#[tlspl(extensible)]
#[repr(u8)]
enum ThingWithData<'a> {
    #[tlspl(discriminant = "Thing::CaseA")]
    CaseA {
        name: std::borrow::Cow<'a, str>,
    },
    #[tlspl(discriminant = "Thing::CaseB")]
    CaseB {
        flag: bool
    },
    #[tlspl(other)]
    Unknown(u8, std::borrow::Cow<'a, [u8]>),
}

On Fields

  • #[tlspl(skip)] - Skips this field during both serialization and deserialization. Requires Default to be implemented on the field since this data format has a fixed data layout.
  • #[tlspl(with = path::to::module)] - Allows to override the ser/deser implementation of the underlying type with a custom implementation. The module should export any of those functions, depending on which trait your're deriving:
    • tlspl_serialized_len(&T) -> usize
    • tlspl_serialize_to(&T, writer) -> TlsplWriteResult<usize>
    • tlspl_deserialize_from(reader) -> TlsplReadResult<T>
  • #[tlspl(select = field.member.thing)] - mirrors the select keyword in TLSPL prose found in specifications.
    • Restrictions
      1. The targeted field must be declared BEFORE this field. The order of declaration matters in TLSPL.
      2. The type of the field with this attribute must be an enum that has #[tlspl(untagged)]
    • Example
#[derive(TlsplAll)]
#[repr(u8)]
pub enum FieldDiscriminant {
    Variant1 = 0x01,
    Variant2 = 0x02,
}

#[derive(TlsplAll)]
#[tlspl(untagged)]
pub enum FieldContents {
    #[tlspl(discriminant = "FieldDiscriminant::Variant1")]
    Variant1,
    #[tlspl(discriminant = "FieldDiscriminant::Variant2")]
    Variant2 {
        thing: bool,
    }
}

#[derive(TlsplAll)]
pub struct ComplexStructure<'a> {
    pub field_type: FieldDiscriminant,
    pub unrelated_field: u64,
    pub another_field: Cow<'a, [u8]>,
    #[tlspl(select = field_type)]
    pub field_contents: FieldContents,
}
  • Enum Variants