le-stream is a small Rust workspace for encoding and decoding values as
little-endian byte streams.
The workspace contains two crates:
le-stream: theno_stdruntime traits and implementations.le-stream-derive: derive macros for structs and represented enums.
Primitive integer and floating-point values use their standard little-endian byte representation. Compound values are encoded by concatenating their fields or elements in declaration order.
Option<T> is compact: None writes no bytes, and Some(value) writes only
value. This is useful for optional trailing data, but it also means Option<T>
does not carry an explicit presence tag.
Derived enums require #[repr(T)] and an explicit discriminant on every
variant. The discriminant is serialized as T, followed by the variant's
associated data.
Allocated collection types do not carry a size prefix by default. Vec<T> and
Box<[T]> deserialize by consuming as many complete T values as possible from
the stream. If a standard allocated collection needs an explicit length, wrap it
in Prefixed<P, D>.
Heapless collection types are length-prefixed. heapless::Vec<T, N, LenT> uses
a little-endian LenT element count, and heapless::String<N, LenT> uses a
little-endian LenT byte count. Deserialization reads that prefix first and
then attempts to consume exactly the number of elements or bytes it describes.
This format is suitable for heapless collections because their types include a
fixed capacity limit.
use le_stream::{FromLeStream, ToLeStream};
let value = 0x1234_u16;
let bytes: Vec<_> = value.to_le_stream().collect();
assert_eq!(bytes, [0x34, 0x12]);
assert_eq!(u16::from_le_stream_exact(bytes.into_iter()), Ok(value));With the derive feature:
use le_stream::{FromLeStream, ToLeStream};
#[derive(Clone, Debug, Eq, PartialEq, FromLeStream, ToLeStream)]
struct Packet {
command: u8,
sequence: u16,
}
#[derive(Clone, Debug, Eq, PartialEq, FromLeStream, ToLeStream)]
#[repr(u8)]
enum Message {
Ping = 1,
Data(Packet) = 2,
}
let message = Message::Data(Packet {
command: 0xA5,
sequence: 0x1234,
});
let bytes: Vec<_> = message.clone().to_le_stream().collect();
assert_eq!(bytes, [2, 0xA5, 0x34, 0x12]);
assert_eq!(Message::from_le_stream_exact(bytes.into_iter()), Ok(message));derive: re-exportFromLeStreamandToLeStreamderive macros fromle-stream-derive.alloc: implementations for unprefixedVec,Box<T>,Box<[T]>, and explicitly prefixed boxed slices.heapless: implementations for length-prefixedheapless::Vec<T, N, LenT>andheapless::String<N, LenT>.macaddr: implementations formacaddr::MacAddr6andmacaddr::MacAddr8.intx: implementations for selectedintxinteger types.
Use the same checks that CI expects:
cargo +nightly fmt --check
cargo clippy --all-features
cargo test --all-features
cargo vet