Skip to main content

cyclone_runtime_rust/
lib.rs

1//! Rust reference runtime for the **Cyclone** binary wire format.
2//!
3//! Cyclone is a wire format specification: the same logical value must produce
4//! exactly one byte sequence, in every language and every implementation. This
5//! crate is the smallest layer that turns primitive values into those bytes and
6//! back.
7//!
8//! ```text
9//! User Model  →  Cyclone Compiler / Derive  →  Generated Codec  →  cyclone-runtime  →  Bytes
10//! ```
11//!
12//! # What this crate does
13//!
14//! - [`Writer`] - appends primitives, strings and byte blobs to a buffer.
15//! - [`Reader`] - reads them back, rejecting malformed input.
16//! - [`Limits`] - allocation guards for untrusted input.
17//! - [`DecodeError`] - every way a byte stream can fail to conform.
18//! - [`Encode`] / [`Decode`] - the traits a codec implements.
19//! - [`to_bytes`] / [`from_bytes`] - the two calls that use those traits.
20//!
21//! A codec reaches these traits from either direction, and the runtime cannot
22//! tell which: `#[derive(Network)]` from `cyclone-codegen-rust` writes an impl
23//! in place, the Cyclone CLI writes one into a `*.codec.rs` file you own and
24//! may edit, and a hand-written impl is just as valid.
25//!
26//! There are no blanket impls of [`Encode`] / [`Decode`] for `u32`, `String`,
27//! `Vec<T>` or any other Rust type. Which Cyclone type a value has is schema
28//! knowledge - `Vec<T>` is an `Array<T>` only because a schema said so - and a
29//! runtime that guessed it from the Rust type would be deciding the wire format
30//! by inference. It writes what a codec tells it to write, nothing more.
31//!
32//! # What this crate does not do
33//!
34//! It does not parse schemas, read annotations, generate codecs, or hold any
35//! registry or type information. It contains no proc macro and no reflection.
36//! Composite types - models, arrays, enums - are the generated codec's job; the
37//! runtime only knows bytes. Two consequences worth stating plainly:
38//!
39//! - There is no `write_model` / `read_model`. A model is written by writing
40//!   its fields in declaration order, with nothing in between (RFC-0002 §5).
41//! - There is no `InvalidEnum` error. Which `u32` values an enum admits is
42//!   schema knowledge, so the generated codec validates it.
43//!
44//! # Format at a glance
45//!
46//! | Type | Bytes |
47//! |------|-------|
48//! | `bool` | 1 - `0x00` or `0x01`, nothing else |
49//! | `i8` / `u8` | 1 |
50//! | `i16` / `u16` | 2, Little Endian |
51//! | `i32` / `u32` | 4, Little Endian |
52//! | `i64` / `u64` | 8, Little Endian |
53//! | `f32` / `f64` | 4 / 8 - raw IEEE 754 bits, never normalized |
54//! | `String` | `u32` UTF-8 **byte** length, then the bytes |
55//! | `Bytes` | `u32` length, then the raw bytes |
56//! | `Array<T>` | `u32` element count, then each element |
57//! | `Enum` | always `u32` |
58//! | `Model` | its fields concatenated in declaration order |
59//!
60//! No varint, no padding, no alignment, no tag id, no object header.
61//!
62//! # Example
63//!
64//! Encoding the model `Item { id: u32, name: String }`, the way a generated
65//! codec would:
66//!
67//! ```
68//! use cyclone_runtime::{from_bytes, to_bytes, Decode, DecodeError, Encode, Reader, Writer};
69//!
70//! struct Item {
71//!     id: u32,
72//!     name: String,
73//! }
74//!
75//! impl Encode for Item {
76//!     fn encode(&self, writer: &mut Writer) {
77//!         writer.write_u32(self.id);
78//!         writer.write_string(&self.name);
79//!     }
80//! }
81//!
82//! impl Decode for Item {
83//!     fn decode(reader: &mut Reader<'_>) -> Result<Self, DecodeError> {
84//!         Ok(Item {
85//!             id: reader.read_u32()?,
86//!             name: reader.read_string()?,
87//!         })
88//!     }
89//! }
90//!
91//! let bytes = to_bytes(&Item { id: 42, name: "Sword".to_owned() });
92//! assert_eq!(
93//!     bytes,
94//!     [0x2A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x53, 0x77, 0x6F, 0x72, 0x64],
95//! );
96//!
97//! let item = from_bytes::<Item>(&bytes)?;
98//! assert_eq!(item.id, 42);
99//! assert_eq!(item.name, "Sword");
100//! # Ok::<(), DecodeError>(())
101//! ```
102//!
103//! # References
104//!
105//! - RFC-0001 - what Cyclone is
106//! - RFC-0002 - the wire format specification
107//! - RFC-0003 - conformance test vectors
108
109#![warn(missing_docs)]
110#![forbid(unsafe_code)]
111
112mod codec;
113mod error;
114mod reader;
115mod writer;
116
117pub use codec::{from_bytes, to_bytes, Decode, Encode};
118pub use error::DecodeError;
119pub use reader::{Limits, Reader};
120pub use writer::Writer;