senax_encoder/lib.rs
1//! # senax-encoder
2//!
3//! A fast, compact, and schema-evolution-friendly binary serialization library for Rust.
4//!
5//! - Supports struct/enum encoding with field/variant IDs for forward/backward compatibility
6//! - Efficient encoding for primitives, collections, Option, String, bytes, and popular crates (chrono, uuid, ulid, rust_decimal, bigdecimal, indexmap, fxhash, ahash, smol_str, serde_json)
7//! - Custom derive macros for ergonomic usage
8//! - Feature-gated support for optional dependencies
9//!
10//! ## Binary Format
11//!
12//! This library uses magic numbers to distinguish between different serialization formats:
13//! - **Encode format**: Starts with magic number `0xA55A` (2 bytes, little-endian)
14//! - Used by `encode()` and `encode_to()` functions
15//! - Supports schema evolution with field IDs and type tags
16//! - **Pack format**: Starts with magic number `0xDADA` (2 bytes, little-endian)
17//! - Used by `pack()` and `pack_to()` functions
18//! - Compact format without schema evolution support
19//!
20//! Magic numbers are only added when using the convenience functions in this library.
21//! Direct trait method calls (`Encoder::encode`, `Packer::pack`) do not include magic numbers.
22//!
23//! ## Attribute Macros
24//!
25//! You can control encoding/decoding behavior using the following attributes:
26//!
27//! - `#[senax(id = N)]` — Assigns a custom field or variant ID (u64). Ensures stable wire format across versions.
28//! - `#[senax(default)]` — If a field is missing during decoding, its value is set to `Default::default()` instead of causing an error. For `Option<T>`, this means `None`.
29//! - `#[senax(skip_encode)]` — This field is not written during encoding. On decode, it is set to `Default::default()`.
30//! - `#[senax(skip_decode)]` — This field is ignored during decoding and always set to `Default::default()`. It is still encoded if present.
31//! - `#[senax(skip_default)]` — This field is not written during encoding if its value equals the default value. On decode, missing fields are set to `Default::default()`.
32//! - `#[senax(rename = "name")]` — Use the given string as the logical field/variant name for ID calculation. Useful for renaming fields/variants while keeping the same wire format.
33//!
34//! ## Feature Flags
35//!
36//! The following optional features enable support for popular crates and types:
37//!
38//! ### External Crate Support
39//! - `chrono` — Enables encoding/decoding of `chrono::DateTime`, `NaiveDate`, and `NaiveTime` types.
40//! - `uuid` — Enables encoding/decoding of `uuid::Uuid`.
41//! - `ulid` — Enables encoding/decoding of `ulid::Ulid` (shares the same tag as UUID for binary compatibility).
42//! - `rust_decimal` — Enables encoding/decoding of `rust_decimal::Decimal`.
43//! - `bigdecimal` — Enables encoding/decoding of `bigdecimal::BigDecimal` (stored as scientific notation string).
44//! - `indexmap` — Enables encoding/decoding of `IndexMap` and `IndexSet` collections.
45//! - `fxhash` — Enables encoding/decoding of `fxhash::FxHashMap` and `fxhash::FxHashSet` (fast hash collections).
46//! - `ahash` — Enables encoding/decoding of `ahash::AHashMap` and `ahash::AHashSet` (high-performance hash collections).
47//! - `smol_str` — Enables encoding/decoding of `smol_str::SmolStr` (small string optimization).
48//! - `serde_json` — Enables encoding/decoding of `serde_json::Value` (JSON values as dynamic type).
49//! - `raw_value` — Enables encoding/decoding of `Box<serde_json::value::RawValue>` (raw JSON strings). Requires `serde_json` feature.
50
51pub mod core;
52mod features;
53
54use bytes::{Buf, BufMut, Bytes, BytesMut};
55pub use senax_encoder_derive::{Decode, Encode, Pack, Unpack};
56use std::collections::HashMap;
57use std::collections::{BTreeMap, BTreeSet, HashSet};
58use std::sync::Arc;
59
60/// Errors that can occur during encoding or decoding operations.
61#[derive(Debug, thiserror::Error)]
62pub enum EncoderError {
63 /// The value could not be encoded (e.g., unsupported type or logic error).
64 #[error("Encode error: {0}")]
65 Encode(String),
66 /// The value could not be decoded (e.g., invalid data, type mismatch, or schema evolution error).
67 #[error("Decode error: {0}")]
68 Decode(String),
69 /// The buffer did not contain enough data to complete the operation.
70 #[error("Insufficient data in buffer")]
71 InsufficientData,
72 /// Struct-specific decode error
73 #[error(transparent)]
74 StructDecode(#[from] StructDecodeError),
75 /// Enum-specific decode error
76 #[error(transparent)]
77 EnumDecode(#[from] EnumDecodeError),
78}
79
80/// The result type used throughout this crate for encode/decode operations.
81///
82/// All `Encode` and `Decode` trait methods return this type.
83pub type Result<T> = std::result::Result<T, EncoderError>;
84
85/// Derive-specific error types for struct operations
86#[derive(Debug, thiserror::Error)]
87pub enum StructDecodeError {
88 #[error("Expected struct named tag ({expected}), got {actual}")]
89 InvalidTag { expected: u8, actual: u8 },
90 #[error("Required field '{field}' not found for struct {struct_name}")]
91 MissingRequiredField {
92 field: &'static str,
93 struct_name: &'static str,
94 },
95 #[error("Field count mismatch for struct {struct_name}: expected {expected}, got {actual}")]
96 FieldCountMismatch {
97 struct_name: &'static str,
98 expected: usize,
99 actual: usize,
100 },
101 #[error("Structure hash mismatch for {struct_name}: expected 0x{expected:016X}, got 0x{actual:016X}")]
102 StructureHashMismatch {
103 struct_name: &'static str,
104 expected: u64,
105 actual: u64,
106 },
107}
108
109/// Derive-specific error types for enum operations
110#[derive(Debug, thiserror::Error)]
111pub enum EnumDecodeError {
112 #[error("Unknown enum tag: {tag} for enum {enum_name}")]
113 UnknownTag { tag: u8, enum_name: &'static str },
114 #[error("Unknown variant ID: 0x{variant_id:016X} for enum {enum_name}")]
115 UnknownVariantId {
116 variant_id: u64,
117 enum_name: &'static str,
118 },
119 #[error("Unknown unit variant ID: 0x{variant_id:016X} for enum {enum_name}")]
120 UnknownUnitVariantId {
121 variant_id: u64,
122 enum_name: &'static str,
123 },
124 #[error("Unknown named variant ID: 0x{variant_id:016X} for enum {enum_name}")]
125 UnknownNamedVariantId {
126 variant_id: u64,
127 enum_name: &'static str,
128 },
129 #[error("Unknown unnamed variant ID: 0x{variant_id:016X} for enum {enum_name}")]
130 UnknownUnnamedVariantId {
131 variant_id: u64,
132 enum_name: &'static str,
133 },
134 #[error("Required field '{field}' not found for variant {enum_name}::{variant_name}")]
135 MissingRequiredField {
136 field: &'static str,
137 enum_name: &'static str,
138 variant_name: &'static str,
139 },
140 #[error("Field count mismatch for variant {enum_name}::{variant_name}: expected {expected}, got {actual}")]
141 FieldCountMismatch {
142 enum_name: &'static str,
143 variant_name: &'static str,
144 expected: usize,
145 actual: usize,
146 },
147 #[error("Structure hash mismatch for variant {enum_name}::{variant_name}: expected 0x{expected:016X}, got 0x{actual:016X}")]
148 StructureHashMismatch {
149 enum_name: &'static str,
150 variant_name: &'static str,
151 expected: u64,
152 actual: u64,
153 },
154}
155
156/// Magic number for encoded format (0xA55A in little-endian)
157const ENCODE_MAGIC: u16 = 0xA55A;
158
159/// Magic number for packed format (0xDADA in little-endian)
160const PACK_MAGIC: u16 = 0xDADA;
161
162/// Convenience function to decode a value from bytes.
163///
164/// This function expects and verifies the encode magic number (0xA55A) at the beginning of the data.
165/// It provides schema evolution support through field IDs and type tags.
166///
167/// # Arguments
168/// * `reader` - The buffer to read the encoded bytes from.
169///
170/// # Example
171/// ```rust
172/// use senax_encoder::{encode, decode, Encode, Decode};
173/// use bytes::BytesMut;
174///
175/// #[derive(Encode, Decode, PartialEq, Debug)]
176/// struct MyStruct {
177/// id: u32,
178/// name: String,
179/// }
180///
181/// let value = MyStruct { id: 42, name: "hello".to_string() };
182/// let mut buf = encode(&value).unwrap();
183/// let decoded: MyStruct = decode(&mut buf).unwrap();
184/// assert_eq!(value, decoded);
185/// ```
186pub fn decode<T: Decoder>(reader: &mut Bytes) -> Result<T> {
187 if reader.remaining() < 2 {
188 return Err(EncoderError::InsufficientData);
189 }
190 let magic = reader.get_u16_le();
191 if magic != ENCODE_MAGIC {
192 return Err(EncoderError::Decode(format!(
193 "Invalid encode magic number: expected 0x{:04X}, got 0x{:04X}",
194 ENCODE_MAGIC, magic
195 )));
196 }
197 T::decode(reader)
198}
199
200/// Convenience function to encode a value to bytes with magic number.
201///
202/// This function adds the encode magic number (0xA55A) at the beginning of the data
203/// and provides schema evolution support through field IDs and type tags.
204///
205/// # Arguments
206/// * `value` - The value to encode.
207///
208/// # Example
209/// ```rust
210/// use senax_encoder::{encode, decode, Encode, Decode};
211/// use bytes::BytesMut;
212///
213/// #[derive(Encode, Decode, PartialEq, Debug)]
214/// struct MyStruct {
215/// id: u32,
216/// name: String,
217/// }
218///
219/// let value = MyStruct { id: 42, name: "hello".to_string() };
220/// let mut buf = encode(&value).unwrap();
221/// let decoded: MyStruct = decode(&mut buf).unwrap();
222/// assert_eq!(value, decoded);
223/// ```
224pub fn encode<T: Encoder>(value: &T) -> Result<Bytes> {
225 let mut writer = BytesMut::new();
226 writer.put_u16_le(ENCODE_MAGIC);
227 value.encode(&mut writer)?;
228 Ok(writer.freeze())
229}
230
231/// Convenience function to encode a value to an existing BytesMut buffer with magic number.
232///
233/// This function adds the encode magic number (0xA55A) at the current position in the buffer
234/// and provides schema evolution support through field IDs and type tags.
235///
236/// # Arguments
237/// * `value` - The value to encode.
238/// * `writer` - The buffer to write the encoded bytes into.
239///
240/// # Example
241/// ```rust
242/// use senax_encoder::{encode_to, decode, Encode, Decode};
243/// use bytes::{BytesMut, Bytes};
244///
245/// #[derive(Encode, Decode, PartialEq, Debug)]
246/// struct MyStruct {
247/// id: u32,
248/// name: String,
249/// }
250///
251/// let value = MyStruct { id: 42, name: "hello".to_string() };
252/// let mut buf = BytesMut::new();
253/// encode_to(&value, &mut buf).unwrap();
254/// let mut data = buf.freeze();
255/// let decoded: MyStruct = decode(&mut data).unwrap();
256/// assert_eq!(value, decoded);
257/// ```
258pub fn encode_to<T: Encoder>(value: &T, writer: &mut BytesMut) -> Result<()> {
259 writer.put_u16_le(ENCODE_MAGIC);
260 value.encode(writer)
261}
262
263/// Trait for types that can be encoded into the senax binary format.
264///
265/// Implement this trait for your type to enable serialization.
266/// Most users should use `#[derive(Encode)]` instead of manual implementation.
267///
268/// # Errors
269/// Returns `EncoderError` if the value cannot be encoded.
270pub trait Encoder {
271 /// Encode the value into the given buffer with schema evolution support.
272 ///
273 /// This method includes field IDs and type tags for forward/backward compatibility.
274 /// Use this when you need schema evolution support.
275 ///
276 /// # Arguments
277 /// * `writer` - The buffer to write the encoded bytes into.
278 fn encode(&self, writer: &mut BytesMut) -> Result<()>;
279
280 /// Returns true if this value equals its default value.
281 /// Used by `#[senax(skip_default)]` attribute to skip encoding default values.
282 fn is_default(&self) -> bool;
283}
284
285/// Trait for types that can be packed into a compact binary format.
286///
287/// This trait provides compact serialization without schema evolution support.
288/// Use this when you need maximum performance and don't require forward/backward compatibility.
289///
290/// # Errors
291/// Returns `EncoderError` if the value cannot be packed.
292pub trait Packer {
293 /// Pack the value into the given buffer without schema evolution support.
294 ///
295 /// This method stores data in a compact format without field IDs or type tags.
296 /// The format is not schema-evolution-friendly but offers better performance.
297 ///
298 /// # Arguments
299 /// * `writer` - The buffer to write the packed bytes into.
300 fn pack(&self, writer: &mut BytesMut) -> Result<()>;
301}
302
303/// Trait for types that can be decoded from the senax binary format.
304///
305/// Implement this trait for your type to enable deserialization.
306/// Most users should use `#[derive(Decode)]` instead of manual implementation.
307///
308/// # Errors
309/// Returns `EncoderError` if the value cannot be decoded or the data is invalid.
310pub trait Decoder: Sized {
311 /// Decode the value from the given buffer with schema evolution support.
312 ///
313 /// This method expects field IDs and type tags for forward/backward compatibility.
314 /// Use this when you need schema evolution support.
315 ///
316 /// # Arguments
317 /// * `reader` - The buffer to read the encoded bytes from.
318 fn decode(reader: &mut Bytes) -> Result<Self>;
319}
320
321/// Trait for types that can be unpacked from a compact binary format.
322///
323/// This trait provides compact deserialization without schema evolution support.
324/// Use this when you need maximum performance and don't require forward/backward compatibility.
325///
326/// # Errors
327/// Returns `EncoderError` if the value cannot be unpacked or the data is invalid.
328pub trait Unpacker: Sized {
329 /// Unpack the value from the given buffer without schema evolution support.
330 ///
331 /// This method reads data from a compact format without field IDs or type tags.
332 /// The format is not schema-evolution-friendly but offers better performance.
333 ///
334 /// # Arguments
335 /// * `reader` - The buffer to read the packed bytes from.
336 fn unpack(reader: &mut Bytes) -> Result<Self>;
337}
338
339/// Convenience function to pack a value to bytes with magic number.
340///
341/// This function adds the pack magic number (0xDADA) at the beginning of the data.
342/// The packed format is compact but not schema-evolution-friendly.
343///
344/// # Arguments
345/// * `value` - The value to pack.
346///
347/// # Example
348/// ```rust
349/// use senax_encoder::{pack, unpack, Pack, Unpack};
350/// use bytes::BytesMut;
351///
352/// #[derive(Pack, Unpack, PartialEq, Debug)]
353/// struct MyStruct {
354/// id: u32,
355/// name: String,
356/// }
357///
358/// let value = MyStruct { id: 42, name: "hello".to_string() };
359/// let mut buf = pack(&value).unwrap();
360/// let decoded: MyStruct = unpack(&mut buf).unwrap();
361/// assert_eq!(value, decoded);
362/// ```
363pub fn pack<T: Packer>(value: &T) -> Result<Bytes> {
364 let mut writer = BytesMut::new();
365 writer.put_u16_le(PACK_MAGIC);
366 value.pack(&mut writer)?;
367 Ok(writer.freeze())
368}
369
370/// Convenience function to pack a value to an existing BytesMut buffer with magic number.
371///
372/// This function adds the pack magic number (0xDADA) at the current position in the buffer.
373/// The packed format is compact but not schema-evolution-friendly.
374///
375/// # Arguments
376/// * `value` - The value to pack.
377/// * `writer` - The buffer to write the packed bytes into.
378///
379/// # Example
380/// ```rust
381/// use senax_encoder::{pack_to, unpack, Pack, Unpack};
382/// use bytes::{BytesMut, Bytes};
383///
384/// #[derive(Pack, Unpack, PartialEq, Debug)]
385/// struct MyStruct {
386/// id: u32,
387/// name: String,
388/// }
389///
390/// let value = MyStruct { id: 42, name: "hello".to_string() };
391/// let mut buf = BytesMut::new();
392/// pack_to(&value, &mut buf).unwrap();
393/// let mut data = buf.freeze();
394/// let decoded: MyStruct = unpack(&mut data).unwrap();
395/// assert_eq!(value, decoded);
396/// ```
397pub fn pack_to<T: Packer>(value: &T, writer: &mut BytesMut) -> Result<()> {
398 writer.put_u16_le(PACK_MAGIC);
399 value.pack(writer)
400}
401
402/// Convenience function to unpack a value from bytes.
403///
404/// This function expects and verifies the pack magic number (0xDADA) at the beginning of the data.
405/// The packed format is compact but not schema-evolution-friendly.
406///
407/// # Arguments
408/// * `reader` - The buffer to read the packed bytes from.
409///
410/// # Example
411/// ```rust
412/// use senax_encoder::{pack, unpack, Pack, Unpack};
413/// use bytes::BytesMut;
414///
415/// #[derive(Pack, Unpack, PartialEq, Debug)]
416/// struct MyStruct {
417/// id: u32,
418/// name: String,
419/// }
420///
421/// let value = MyStruct { id: 42, name: "hello".to_string() };
422/// let mut buf = pack(&value).unwrap();
423/// let decoded: MyStruct = unpack(&mut buf).unwrap();
424/// assert_eq!(value, decoded);
425/// ```
426pub fn unpack<T: Unpacker>(reader: &mut Bytes) -> Result<T> {
427 if reader.remaining() < 2 {
428 return Err(EncoderError::InsufficientData);
429 }
430 let magic = reader.get_u16_le();
431 if magic != PACK_MAGIC {
432 return Err(EncoderError::Decode(format!(
433 "Invalid pack magic number: expected 0x{:04X}, got 0x{:04X}",
434 PACK_MAGIC, magic
435 )));
436 }
437 T::unpack(reader)
438}