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, indexmap, fxhash, ahash, smol_str)
7//! - Custom derive macros for ergonomic usage
8//! - Feature-gated support for optional dependencies
9//!
10//! ## Attribute Macros
11//!
12//! You can control encoding/decoding behavior using the following attributes:
13//!
14//! - `#[senax(id = N)]` — Assigns a custom field or variant ID (u64). Ensures stable wire format across versions.
15//! - `#[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`.
16//! - `#[senax(skip_encode)]` — This field is not written during encoding. On decode, it is set to `Default::default()`.
17//! - `#[senax(skip_decode)]` — This field is ignored during decoding and always set to `Default::default()`. It is still encoded if present.
18//! - `#[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()`.
19//! - `#[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.
20//!
21//! ## Feature Flags
22//!
23//! The following optional features enable support for popular crates and types:
24//!
25//! - `chrono` — Enables encoding/decoding of `chrono::DateTime`, `NaiveDate`, and `NaiveTime` types.
26//! - `uuid` — Enables encoding/decoding of `uuid::Uuid`.
27//! - `ulid` — Enables encoding/decoding of `ulid::Ulid` (shares the same tag as UUID for binary compatibility).
28//! - `rust_decimal` — Enables encoding/decoding of `rust_decimal::Decimal`.
29//! - `indexmap` — Enables encoding/decoding of `IndexMap` and `IndexSet` collections.
30//! - `fxhash` — Enables encoding/decoding of `fxhash::FxHashMap` and `fxhash::FxHashSet` (fast hash collections).
31//! - `ahash` — Enables encoding/decoding of `ahash::AHashMap` and `ahash::AHashSet` (high-performance hash collections).
32//! - `smol_str` — Enables encoding/decoding of `smol_str::SmolStr` (small string optimization).
33//! - `serde_json` — Enables encoding/decoding of `serde_json::Value` (JSON values as dynamic type).
34//!
35//! ## Example
36//! ```rust
37//! use senax_encoder::{Encode, Decode};
38//! use bytes::BytesMut;
39//!
40//! #[derive(Encode, Decode, PartialEq, Debug)]
41//! struct MyStruct {
42//! id: u32,
43//! name: String,
44//! }
45//!
46//! let value = MyStruct { id: 42, name: "hello".to_string() };
47//! let mut buf = senax_encoder::encode(&value).unwrap();
48//! let decoded: MyStruct = senax_encoder::decode(&mut buf).unwrap();
49//! assert_eq!(value, decoded);
50//! ```
51
52pub mod core;
53mod features;
54
55use bytes::{Buf, BufMut, Bytes, BytesMut};
56pub use senax_encoder_derive::{Decode, Encode};
57use std::collections::HashMap;
58use std::collections::{BTreeMap, BTreeSet, HashSet};
59use std::sync::Arc;
60use thiserror::Error;
61
62/// Error type for all encoding and decoding operations in this crate.
63///
64/// This error type is returned by all `Encode` and `Decode` trait methods.
65/// It covers I/O errors, encoding/decoding logic errors, and buffer underflow.
66#[derive(Debug, Error)]
67pub enum EncoderError {
68 /// The value could not be encoded (e.g., unsupported type or logic error).
69 #[error("Encode error: {0}")]
70 Encode(String),
71 /// The value could not be decoded (e.g., invalid data, type mismatch, or schema evolution error).
72 #[error("Decode error: {0}")]
73 Decode(String),
74 /// The buffer did not contain enough data to complete the operation.
75 #[error("Insufficient data in buffer")]
76 InsufficientData,
77}
78
79/// The result type used throughout this crate for encode/decode operations.
80///
81/// All `Encode` and `Decode` trait methods return this type.
82pub type Result<T> = std::result::Result<T, EncoderError>;
83
84/// Convenience function to decode a value from bytes.
85///
86/// This is equivalent to calling `T::decode(reader)` but provides a more ergonomic API.
87///
88/// # Arguments
89/// * `reader` - The buffer to read the encoded bytes from.
90///
91/// # Example
92/// ```rust
93/// use senax_encoder::{encode, decode, Encode, Decode};
94/// use bytes::BytesMut;
95///
96/// #[derive(Encode, Decode, PartialEq, Debug)]
97/// struct MyStruct {
98/// id: u32,
99/// name: String,
100/// }
101///
102/// let value = MyStruct { id: 42, name: "hello".to_string() };
103/// let mut buf = encode(&value).unwrap();
104/// let decoded: MyStruct = decode(&mut buf).unwrap();
105/// assert_eq!(value, decoded);
106/// ```
107pub fn decode<T: Decoder>(reader: &mut Bytes) -> Result<T> {
108 T::decode(reader)
109}
110
111/// Convenience function to encode a value to bytes.
112///
113/// This is equivalent to calling `value.encode(writer)` but provides a more ergonomic API.
114///
115/// # Arguments
116/// * `value` - The value to encode.
117/// * `writer` - The buffer to write the encoded bytes into.
118///
119/// # Example
120/// ```rust
121/// use senax_encoder::{encode, decode, Encode, Decode};
122/// use bytes::BytesMut;
123///
124/// #[derive(Encode, Decode, PartialEq, Debug)]
125/// struct MyStruct {
126/// id: u32,
127/// name: String,
128/// }
129///
130/// let value = MyStruct { id: 42, name: "hello".to_string() };
131/// let mut buf = encode(&value).unwrap();
132/// let decoded: MyStruct = decode(&mut buf).unwrap();
133/// assert_eq!(value, decoded);
134/// ```
135pub fn encode<T: Encoder>(value: &T) -> Result<Bytes> {
136 let mut writer = BytesMut::new();
137 value.encode(&mut writer)?;
138 Ok(writer.freeze())
139}
140
141/// Trait for types that can be encoded into the senax binary format.
142///
143/// Implement this trait for your type to enable serialization.
144/// Most users should use `#[derive(Encode)]` instead of manual implementation.
145///
146/// # Errors
147/// Returns `EncoderError` if the value cannot be encoded.
148pub trait Encoder {
149 /// Encode the value into the given buffer.
150 ///
151 /// # Arguments
152 /// * `writer` - The buffer to write the encoded bytes into.
153 fn encode(&self, writer: &mut BytesMut) -> Result<()>;
154
155 /// Returns true if this value equals its default value.
156 /// Used by `#[senax(skip_default)]` attribute to skip encoding default values.
157 fn is_default(&self) -> bool;
158}
159
160/// Trait for types that can be decoded from the senax binary format.
161///
162/// Implement this trait for your type to enable deserialization.
163/// Most users should use `#[derive(Decode)]` instead of manual implementation.
164///
165/// # Errors
166/// Returns `EncoderError` if the value cannot be decoded or the data is invalid.
167pub trait Decoder: Sized {
168 /// Decode the value from the given buffer.
169 ///
170 /// # Arguments
171 /// * `reader` - The buffer to read the encoded bytes from.
172 fn decode(reader: &mut Bytes) -> Result<Self>;
173}