serde_store
A Rust implementation of the Haskell store binary serialization format using Serde.
Overview
This library provides Serde serializers and deserializers that are compatible with Haskell's store library. It enables inter-operability between Rust and Haskell programs using a compact, efficient binary format.
Features
- ✅ Full Haskell
storecompatibility: Binary format matches Haskell store encoding - ✅ Little-endian encoding: Optimized for modern architectures
- ✅ Serde integration: Works with any type implementing Serde traits
- ✅ Idempotent: Serialize-deserialize roundtrips preserve data exactly
- ✅ Type-safe: Leverages Rust's type system for correctness
- ✅ Comprehensive tests: Extensive test coverage including roundtrip tests
- ✅ Optional features: Support for
EitherandSmolStrtypes via cargo features
Format Specification
The format follows Haskell store conventions:
Primitives
- Booleans:
u8(0 = false, 1 = true) - Integers: Little-endian encoding (i8, i16, i32, i64, u8, u16, u32, u64)
- Floats: Little-endian encoding (f32, f64)
Strings and Bytes
- String/Text:
u64length (LE) + UTF-8 bytes - Bytes:
u64length (LE) + raw bytes - Char: Encoded as a single-character UTF-8 string
Options
- None:
u8tag = 0 - Some(x):
u8tag = 1, followed by serialized value
Collections
- Vec/Array/Seq:
u64length (LE) + elements - Map:
u64count (LE) + key-value pairs - Set:
u64count (LE) + elements
Tuples
- Elements serialized sequentially (no length prefix)
- Supported up to 7 elements (matching Haskell Store)
Structs and Tuples (Products)
- Fields are serialized sequentially
- No length prefix (length is implicit from schema)
Enums (Sum Types)
- Discriminant:
u64variant index (LE) - Followed by variant data (if any)
Usage
Add to your Cargo.toml:
[]
= "0.1"
= { = "1.0", = ["derive"] }
Optional Features
# Enable Either support (Haskell's Either a b)
= { = "0.1", = ["either"] }
# Enable SmolStr support (small string optimization)
= { = "0.1", = ["smol_str"] }
# Enable all features
= { = "0.1", = ["either", "smol_str"] }
Basic Example
use ;
use ;
Complex Types
use BTreeMap;
use ;
use ;
let mut settings = new;
settings.insert;
settings.insert;
let config = Config ;
let bytes = to_bytes.unwrap;
let decoded: Config = from_bytes.unwrap;
assert_eq!;
Haskell Interoperability
This implementation is designed to be binary-compatible with Haskell's store library.
Haskell Side
data Person = Person
{ name :: Text
, age :: Word32
, email :: Maybe Text
}
-- Encode in Haskell
bytes = encode (Person "Alice" 30 (Just "alice@example.com"))
-- Decode in Rust
-- The same bytes can be decoded using serde_store
Rust Side
// Decode bytes from Haskell
let person: Person = from_bytes.unwrap;
// Encode for Haskell
let bytes = to_bytes.unwrap;
Using Either (Optional Feature)
use Either;
use ;
// Either works like Haskell's Either a b
let left: = Left;
let right: = Right;
let bytes = to_bytes.unwrap;
let decoded: = from_bytes.unwrap;
Using SmolStr (Optional Feature)
use SmolStr;
use ;
// SmolStr is binary-compatible with String
let s = new;
let bytes = to_bytes.unwrap;
// Can deserialize as String
let as_string: String = from_bytes.unwrap;
// Or as SmolStr
let as_smolstr: SmolStr = from_bytes.unwrap;
Type Mappings
| Rust Type | Haskell Type | Notes |
|---|---|---|
bool |
Bool |
|
u8, u16, u32, u64 |
Word8, Word16, Word32, Word64 |
|
i8, i16, i32, i64 |
Int8, Int16, Int32, Int64 |
|
f32, f64 |
Float, Double |
|
String |
Text |
|
Vec<u8> |
ByteString |
|
Option<T> |
Maybe T |
|
Vec<T> |
[T] or Vector T |
|
(T1, T2, ...) |
(T1, T2, ...) |
Up to 7 elements |
HashMap<K, V> |
HashMap K V |
|
BTreeMap<K, V> |
Map K V |
|
HashSet<T> |
HashSet T |
|
BTreeSet<T> |
Set T |
|
Either<L, R> |
Either L R |
Requires either feature |
SmolStr |
Text |
Requires smol_str feature, binary-compatible with String |
| Struct | Product type | |
| Enum | Sum type |
Implementation Details
Data Types Supported
The following Rust types are fully supported:
- ✅ All primitive numeric types
- ✅ Strings (UTF-8)
- ✅ Byte arrays and vectors
- ✅ Options (
Option<T>) - ✅ Tuples (1-7 elements, matching Haskell Store support)
- ✅ Structs (named and tuple structs)
- ✅ Enums (unit, newtype, tuple, and struct variants)
- ✅ Collections (Vec, HashMap, BTreeMap, HashSet, BTreeSet)
- ✅ Arrays
- ✅ Either type (with
eitherfeature) - ✅ SmolStr (with
smol_strfeature, binary-compatible with String) - ✅ Any type implementing
Serialize/Deserialize
Limitations
- Endianness: Only little-endian is supported (matching modern Haskell
store) - Schema evolution: Changes to data types require coordinated updates
- Self-describing: The format is NOT self-describing; both sides must know the schema
- No versioning: No built-in version negotiation
Testing
The library includes comprehensive tests:
# Run all tests
# Run with all features
# Run with verbose output
# Run specific test suites
Test Coverage
- Primitive types: All numeric types, bools, chars
- Strings: ASCII, Unicode, empty strings, long strings
- Collections: Vectors, maps, sets (both hash-based and tree-based)
- Complex types: Nested structs, enums with data, options
- Tuples: All sizes (1-7 elements), nested tuples, tuples with complex types
- Either: Left/Right variants, nested Either, with Options/Vecs (requires
eitherfeature) - SmolStr: Short/long strings, Unicode, interchangeability with String (requires
smol_strfeature) - Idempotence: Serialize-deserialize cycles preserve data exactly
- Binary stability: Same value always produces same bytes
- Haskell interop: Full test suite with Haskell Store echo server
Performance
The format is designed for efficiency:
- Zero-copy where possible (planned future optimization)
- Direct memory representations for primitives
- Compact encoding (no metadata overhead)
- Predictable size calculation for fixed-size types
Contributing
Contributions are welcome! Please ensure:
- All tests pass:
cargo test - Code is formatted:
cargo fmt - No clippy warnings:
cargo clippy - Add tests for new features
License
This project follows the same license as the Haskell store library (MIT).
References
Version History
0.2.2 (Initial Release)
- Full serializer implementation
- Full deserializer implementation
- Comprehensive test suite
- Haskell
storeformat compatibility - Support for all common Rust types
- Tuple support (1-7 elements)
- Optional
Eithersupport (via cargo feature) - Optional
SmolStrsupport (via cargo feature) - Full Haskell interoperability test suite