Skip to main content

Crate reliakit_derive

Crate reliakit_derive 

Source
Expand description

Derive macros for reliakit traits.

This crate provides #[derive(...)] support for the trait pairs defined by other reliakit-* crates. It is written using only the standard proc_macro API and pulls in no third-party crates. To stay free of a full Rust-grammar parser, it reads only what the generated code needs (the type name and its field shape) and rejects constructs it does not yet handle with a clear compile error rather than guessing.

§Supported types

  • structs with named fields
  • tuple structs
  • unit structs
  • enums with unit, tuple, and struct variants

Unions, generic types, generic enums, enums with explicit discriminants or a #[repr(...)], and empty enums are rejected with a compile error. The JSON derives currently cover structs only; enums are rejected for now. The CSV derives cover only structs with named fields, since CSV columns need names.

§Field attributes

Named fields accept two #[reliakit(...)] options that affect the JSON and CSV derives:

  • #[reliakit(rename = "...")] uses the given string as the JSON object key or CSV header for that field instead of its Rust name.
  • #[reliakit(skip)] omits the field from the JSON object / CSV row on encode and fills it with Default::default() on decode (so the field’s type must implement Default).

The canonical codec (CanonicalEncode/CanonicalDecode) ignores both: it is positional binary with no field names, so it always encodes and decodes every field in declaration order, and its wire format is unaffected by these attributes.

use reliakit_json::{from_json_str, to_json_string};
use reliakit_derive::{JsonDecode, JsonEncode};

#[derive(Debug, PartialEq, JsonEncode, JsonDecode)]
struct Row {
    #[reliakit(rename = "id")]
    identifier: u32,
    #[reliakit(skip)]
    cached: u8,
}

let json = to_json_string(&Row { identifier: 7, cached: 99 });
assert_eq!(json, r#"{"id":7}"#); // renamed key, `cached` omitted
assert_eq!(
    from_json_str::<Row>(&json).unwrap(),
    Row { identifier: 7, cached: 0 } // `cached` defaulted on decode
);

§reliakit-codec

CanonicalEncode and CanonicalDecode generate implementations of the same-named traits from reliakit-codec, encoding each field in declaration order. The derived code is exactly what a handwritten implementation would be: one encode/decode call per field, in order.

use reliakit_codec::{decode_from_slice_exact, encode_to_vec};
use reliakit_derive::{CanonicalDecode, CanonicalEncode};

#[derive(Debug, PartialEq, CanonicalEncode, CanonicalDecode)]
struct Point {
    x: u16,
    y: u16,
}

let encoded = encode_to_vec(&Point { x: 10, y: 20 }).unwrap();
assert_eq!(encoded, [10, 0, 20, 0]);
assert_eq!(decode_from_slice_exact::<Point>(&encoded).unwrap(), Point { x: 10, y: 20 });

Enums are supported too. Each variant is tagged by its zero-based declaration index, encoded as a little-endian u32, followed by the variant’s fields in declaration order:

use reliakit_codec::{decode_from_slice_exact, encode_to_vec};
use reliakit_derive::{CanonicalDecode, CanonicalEncode};

#[derive(Debug, PartialEq, CanonicalEncode, CanonicalDecode)]
enum Message {
    Ping,
    Pong,
}

assert_eq!(encode_to_vec(&Message::Ping).unwrap(), [0, 0, 0, 0]);
assert_eq!(encode_to_vec(&Message::Pong).unwrap(), [1, 0, 0, 0]);
assert_eq!(decode_from_slice_exact::<Message>(&[1, 0, 0, 0]).unwrap(), Message::Pong);

§reliakit-json

JsonEncode and JsonDecode generate implementations of the same-named reliakit-json traits. A struct with named fields becomes a JSON object in declaration order, a tuple struct becomes an array, and a unit struct becomes null. Decoding is strict; unknown object fields are ignored.

use reliakit_derive::{JsonDecode, JsonEncode};
use reliakit_json::{from_json_str, to_json_string};

#[derive(Debug, PartialEq, JsonEncode, JsonDecode)]
struct Point {
    x: u16,
    y: u16,
}

let json = to_json_string(&Point { x: 10, y: 20 });
assert_eq!(json, r#"{"x":10,"y":20}"#);
assert_eq!(from_json_str::<Point>(&json).unwrap(), Point { x: 10, y: 20 });

§reliakit-csv

CsvEncode and CsvDecode generate implementations of the same-named reliakit-csv traits. A struct with named fields becomes a CSV row, one column per field in declaration order, with the field names as the header. Because CSV columns need names, only structs with named fields are supported: tuple structs, unit structs, and enums are rejected. Decoding is strict: the row must have one field per struct field, and each must parse.

use reliakit_csv::{from_csv_str, to_csv_string};
use reliakit_derive::{CsvDecode, CsvEncode};

#[derive(Debug, PartialEq, CsvEncode, CsvDecode)]
struct Row {
    id: u32,
    name: String,
}

let rows = vec![Row { id: 1, name: "ada".into() }];
let csv = to_csv_string(&rows);
assert_eq!(csv, "id,name\r\n1,ada\r\n");
assert_eq!(from_csv_str::<Row>(&csv).unwrap(), rows);

§Deriving through the umbrella crate

By default the generated code refers to the standalone crates (::reliakit_csv, ::reliakit_codec, ::reliakit_json), so those must be direct dependencies of the crate that uses the derives. If you instead depend only on the umbrella reliakit crate (which re-exports them as reliakit::csv, reliakit::codec, reliakit::json), add #[reliakit(crate = "reliakit")] so the derive resolves through it:

use reliakit::derive::{CsvDecode, CsvEncode};

#[derive(CsvEncode, CsvDecode)]
#[reliakit(crate = "reliakit")]
struct Row { id: u32, name: String }

The value is any path whose csv/codec/json submodules re-export the corresponding crates (the umbrella’s layout). Omit the attribute to keep the standalone paths.

Derive Macros§

CanonicalDecode
Derives reliakit_codec::CanonicalDecode, decoding each field in declaration order (for enums, the variant tag first).
CanonicalEncode
Derives reliakit_codec::CanonicalEncode, encoding each field in declaration order (for enums, the variant tag first).
CsvDecode
Derives reliakit_csv::CsvDecode, the inverse of CsvEncode. Decoding is strict: the row must have exactly one field per struct field, and each field must parse into its target type.
CsvEncode
Derives reliakit_csv::CsvEncode: a struct with named fields becomes a row, one column per field in declaration order, with the field names as the header.
JsonDecode
Derives reliakit_json::JsonDecode, the inverse of JsonEncode. Decoding is strict: the JSON shape must match, and required object fields must be present; unknown object fields are ignored.
JsonEncode
Derives reliakit_json::JsonEncode: a struct with named fields becomes a JSON object (in declaration order), a tuple struct becomes a JSON array, and a unit struct becomes null.