pack_io/lib.rs
1//! # pack-io
2//!
3//! Compact binary wire format for Rust. Combines speed, schema evolution, and
4//! zero-copy deserialization under a single coherent contract.
5//!
6//! ## At a glance (v0.3.0)
7//!
8//! - **Tier 1** — [`encode`] and [`decode`]: one line each direction.
9//! - **Tier 2** —
10//! - [`Encoder`] / [`Decoder`] for in-memory buffers.
11//! - [`IoEncoder`] / [`IoDecoder`] for `std::io::Write` / `Read` streams
12//! (`std`-gated).
13//! - [`encode_into`] / [`decode_from`] convenience helpers over Read/Write.
14//! - **Tier 3** — implement [`Serialize`] / [`Deserialize`] on your own
15//! types. Both traits are generic over the [`Encode`] / [`Decode`]
16//! behaviour traits, so one impl works through every encoder / decoder
17//! the crate ships.
18//!
19//! ## Primitive support
20//!
21//! Integers (`u8` … `u128`, `i8` … `i128`, `usize` / `isize`), `bool`,
22//! `f32`, `f64`, `String` / `&str`, fixed-size arrays `[T; N]`, tuples of
23//! arity 1…12, `Option<T>`, `Result<T, E>`, and `()`.
24//!
25//! ## Collection support (new in v0.3)
26//!
27//! `Vec<T>` / `&[T]`, `BTreeMap<K, V>`, `BTreeSet<T>`, and (with the default
28//! `std` feature) `HashMap<K, V>` and `HashSet<T>`. **Hash-based collections
29//! encode in canonical key-sorted order** so that hashing, signing, or
30//! content-addressing the output is safe regardless of insertion order or
31//! hash randomisation.
32//!
33//! ## Wire-format freeze
34//!
35//! Starting at `v0.3.0` the wire format is **frozen** for the `1.x` line.
36//! See [`docs/WIRE_FORMAT.md`](https://github.com/jamesgober/pack-io/blob/main/docs/WIRE_FORMAT.md)
37//! for the normative byte-level spec.
38//!
39//! ## Quick start
40//!
41//! ```
42//! use pack_io::{encode, decode};
43//!
44//! let bytes = encode(&(7_u64, true, String::from("hello"))).unwrap();
45//! let back: (u64, bool, String) = decode(&bytes).unwrap();
46//! assert_eq!(back, (7, true, String::from("hello")));
47//! ```
48//!
49//! ## Invariants
50//!
51//! - **Round-trip integrity** — `decode(encode(v)) == v` for every supported
52//! type, under any input.
53//! - **Determinism** — the same value always produces the same bytes,
54//! regardless of insertion order, platform, or build flags.
55//! - **Safe decode** — no panic, no unbounded allocation, no read past the
56//! input, on any byte sequence.
57//! - **Wire-format stability** — frozen at `0.3.0`; any `1.x` decoder reads
58//! any `1.x`-or-earlier encoding.
59//!
60//! ## `no_std`
61//!
62//! `pack-io` is `no_std`-capable. The default build enables `std` for the
63//! [`std::error::Error`] impl, `HashMap` / `HashSet` integration, and the
64//! [`io`] module. Disable the default feature to compile against `core` +
65//! `alloc` only:
66//!
67//! ```toml
68//! pack-io = { version = "0.3", default-features = false }
69//! ```
70
71#![cfg_attr(not(feature = "std"), no_std)]
72#![cfg_attr(docsrs, feature(doc_cfg))]
73#![deny(missing_docs)]
74#![deny(unused_must_use)]
75#![deny(clippy::todo)]
76#![deny(clippy::unimplemented)]
77#![deny(clippy::print_stdout)]
78#![deny(clippy::print_stderr)]
79#![deny(clippy::dbg_macro)]
80#![deny(clippy::undocumented_unsafe_blocks)]
81#![forbid(unsafe_code)]
82
83extern crate alloc;
84
85#[cfg(feature = "std")]
86extern crate std;
87
88mod codec;
89mod error;
90mod impls;
91#[cfg(feature = "std")]
92pub mod io;
93mod traits;
94mod varint;
95
96pub use crate::codec::{Config, Decode, Decoder, Encode, Encoder, decode, encode};
97pub use crate::error::{Result, SerialError};
98pub use crate::traits::{Deserialize, Serialize};
99
100#[cfg(feature = "std")]
101pub use crate::io::{IoDecoder, IoEncoder, decode_from, encode_into};
102
103/// Semantic version of this crate, as declared in `Cargo.toml`.
104///
105/// # Examples
106///
107/// ```
108/// assert!(pack_io::VERSION.starts_with("0."));
109/// ```
110pub const VERSION: &str = env!("CARGO_PKG_VERSION");
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn version_matches_cargo_manifest() {
118 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
119 }
120
121 #[test]
122 fn tier_one_encode_decode_round_trips_a_tuple() {
123 let bytes = encode(&(1_u64, true, String::from("hello"))).expect("encode");
124 let back: (u64, bool, String) = decode(&bytes).expect("decode");
125 assert_eq!(back, (1, true, String::from("hello")));
126 }
127}