| Crates.io | serde_store |
| lib.rs | serde_store |
| version | 0.2.2 |
| created_at | 2025-11-17 07:20:58.161733+00 |
| updated_at | 2025-11-17 07:56:14.843581+00 |
| description | A Rust implementation of the Haskell store binary serialization format using Serde |
| homepage | https://github.com/awgn/serde_store |
| repository | https://github.com/awgn/serde_store |
| max_upload_size | |
| id | 1936375 |
| size | 170,610 |
A Rust implementation of the Haskell store binary serialization format using Serde.
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.
store compatibility: Binary format matches Haskell store encodingEither and SmolStr types via cargo featuresThe format follows Haskell store conventions:
u8 (0 = false, 1 = true)u64 length (LE) + UTF-8 bytesu64 length (LE) + raw bytesu8 tag = 0u8 tag = 1, followed by serialized valueu64 length (LE) + elementsu64 count (LE) + key-value pairsu64 count (LE) + elementsu64 variant index (LE)Add to your Cargo.toml:
[dependencies]
serde_store = "0.1"
serde = { version = "1.0", features = ["derive"] }
# Enable Either support (Haskell's Either a b)
serde_store = { version = "0.1", features = ["either"] }
# Enable SmolStr support (small string optimization)
serde_store = { version = "0.1", features = ["smol_str"] }
# Enable all features
serde_store = { version = "0.1", features = ["either", "smol_str"] }
use serde::{Serialize, Deserialize};
use serde_store::{to_bytes, from_bytes};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Person {
name: String,
age: u32,
email: Option<String>,
}
fn main() {
let person = Person {
name: "Alice".to_string(),
age: 30,
email: Some("alice@example.com".to_string()),
};
// Serialize
let bytes = to_bytes(&person).unwrap();
// Deserialize
let decoded: Person = from_bytes(&bytes).unwrap();
assert_eq!(person, decoded);
}
use std::collections::BTreeMap;
use serde::{Serialize, Deserialize};
use serde_store::{to_bytes, from_bytes};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Status {
Active,
Inactive { reason: String },
Pending(u32),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Config {
id: u64,
name: String,
settings: BTreeMap<String, i32>,
status: Status,
}
let mut settings = BTreeMap::new();
settings.insert("timeout".to_string(), 30);
settings.insert("retries".to_string(), 3);
let config = Config {
id: 123,
name: "production".to_string(),
settings,
status: Status::Active,
};
let bytes = to_bytes(&config).unwrap();
let decoded: Config = from_bytes(&bytes).unwrap();
assert_eq!(config, decoded);
This implementation is designed to be binary-compatible with Haskell's store library.
{-# LANGUAGE DeriveGeneric #-}
import Data.Store
import GHC.Generics
data Person = Person
{ name :: Text
, age :: Word32
, email :: Maybe Text
} deriving (Generic, Show)
instance Store Person
-- Encode in Haskell
bytes = encode (Person "Alice" 30 (Just "alice@example.com"))
-- Decode in Rust
-- The same bytes can be decoded using serde_store
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u32,
email: Option<String>,
}
// Decode bytes from Haskell
let person: Person = from_bytes(&haskell_bytes).unwrap();
// Encode for Haskell
let bytes = to_bytes(&person).unwrap();
use either::Either;
use serde_store::{to_bytes, from_bytes};
// Either works like Haskell's Either a b
let left: Either<i32, String> = Either::Left(42);
let right: Either<i32, String> = Either::Right("error".to_string());
let bytes = to_bytes(&left).unwrap();
let decoded: Either<i32, String> = from_bytes(&bytes).unwrap();
use smol_str::SmolStr;
use serde_store::{to_bytes, from_bytes};
// SmolStr is binary-compatible with String
let s = SmolStr::new("hello");
let bytes = to_bytes(&s).unwrap();
// Can deserialize as String
let as_string: String = from_bytes(&bytes).unwrap();
// Or as SmolStr
let as_smolstr: SmolStr = from_bytes(&bytes).unwrap();
| 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 |
The following Rust types are fully supported:
Option<T>)either feature)smol_str feature, binary-compatible with String)Serialize/Deserializestore)The library includes comprehensive tests:
# Run all tests
cargo test
# Run with all features
cargo test --all-features
# Run with verbose output
cargo test -- --nocapture
# Run specific test suites
cargo test --test roundtrip_tests
cargo test --test tuple_tests
cargo test --features either --test either_tests
cargo test --features smol_str --test smolstr_tests
either feature)smol_str feature)The format is designed for efficiency:
Contributions are welcome! Please ensure:
cargo testcargo fmtcargo clippyThis project follows the same license as the Haskell store library (MIT).
store format compatibilityEither support (via cargo feature)SmolStr support (via cargo feature)