serde_toon/
lib.rs

1//! # serde_toon
2//!
3//! A Serde-compatible serialization library for the TOON (Token-Oriented Object Notation) format.
4//!
5//! ## What is TOON?
6//!
7//! TOON is a compact, human-readable data format specifically designed for efficient communication
8//! with Large Language Models (LLMs). It achieves 30-60% fewer tokens than equivalent JSON while
9//! maintaining readability and structure.
10//!
11//! ## Key Features
12//!
13//! - **Token-Efficient**: Minimalist syntax reduces token count by eliminating unnecessary braces,
14//!   brackets, and quotes
15//! - **Tabular Arrays**: Homogeneous object arrays serialize as compact tables with headers
16//! - **Serde Compatible**: Works seamlessly with existing Rust types via `#[derive(Serialize, Deserialize)]`
17//! - **Type Safe**: Statically typed with comprehensive error reporting
18//! - **No Unsafe Code**: Written entirely in safe Rust with zero unsafe blocks
19//!
20//! ## Quick Start
21//!
22//! Add this to your `Cargo.toml`:
23//!
24//! ```toml
25//! [dependencies]
26//! serde = { version = "1.0", features = ["derive"] }
27//! serde_toon = "0.2"
28//! ```
29//!
30//! ### Basic Serialization and Deserialization
31//!
32//! ```rust
33//! use serde::{Deserialize, Serialize};
34//! use serde_toon::{to_string, from_str};
35//!
36//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
37//! struct User {
38//!     id: u32,
39//!     name: String,
40//!     active: bool,
41//! }
42//!
43//! let user = User {
44//!     id: 123,
45//!     name: "Alice".to_string(),
46//!     active: true,
47//! };
48//!
49//! // Serialize to TOON format
50//! let toon_string = to_string(&user).unwrap();
51//! // Output: "id: 123\nname: Alice\nactive: true"
52//!
53//! // Deserialize back
54//! let user_back: User = from_str(&toon_string).unwrap();
55//! assert_eq!(user, user_back);
56//! ```
57//!
58//! ### Working with Arrays (Tabular Format)
59//!
60//! Arrays of homogeneous objects automatically serialize as space-efficient tables:
61//!
62//! ```rust
63//! use serde::{Deserialize, Serialize};
64//! use serde_toon::to_string;
65//!
66//! #[derive(Serialize, Deserialize)]
67//! struct Product {
68//!     id: u32,
69//!     name: String,
70//!     price: f64,
71//! }
72//!
73//! let products = vec![
74//!     Product { id: 1, name: "Widget".to_string(), price: 9.99 },
75//!     Product { id: 2, name: "Gadget".to_string(), price: 14.99 },
76//! ];
77//!
78//! let toon = to_string(&products).unwrap();
79//! // Output: "[2]{id,name,price}:\n  1,Widget,9.99\n  2,Gadget,14.99"
80//! ```
81//!
82//! ### Dynamic Values with toon! Macro
83//!
84//! ```rust
85//! use serde_toon::{toon, Value};
86//!
87//! let data = toon!({
88//!     "name": "Alice",
89//!     "age": 30,
90//!     "tags": ["rust", "serde", "llm"]
91//! });
92//!
93//! if let Value::Object(obj) = data {
94//!     assert_eq!(obj.get("name").and_then(|v| v.as_str()), Some("Alice"));
95//! }
96//! ```
97//!
98//! ## Performance Characteristics
99//!
100//! - **Serialization**: O(n) where n is the number of fields/elements
101//! - **Deserialization**: O(n) with single-pass parsing
102//! - **Memory**: Pre-allocated buffers minimize reallocations
103//! - **Token Count**: 30-60% reduction vs JSON for typical structured data
104//!
105//! ## Safety Guarantees
106//!
107//! - No `unsafe` code blocks
108//! - All array indexing is bounds-checked
109//! - Proper error propagation with `Result` types
110//! - No panics in public API (except for logic errors that indicate bugs)
111//!
112//! ## Format Specification
113//!
114//! For the complete TOON format specification, see the [`spec`] module documentation.
115//!
116//! External reference: <https://github.com/johannschopplich/toon>
117//!
118//! ## Examples
119//!
120//! See the `examples/` directory for focused, production-ready examples:
121//!
122//! - **`simple.rs`** - Your first TOON experience (basic serialization)
123//! - **`macro.rs`** - Building values with the toon! macro
124//! - **`tabular_arrays.rs`** - TOON's tabular feature for repeated structures
125//! - **`dynamic_values.rs`** - Working with Value dynamically
126//! - **`custom_options.rs`** - Customizing delimiters and formatting
127//! - **`token_efficiency.rs`** - TOON vs JSON comparison
128//!
129//! Run any example with: `cargo run --example <name>`
130
131pub mod de;
132pub mod error;
133pub mod macros;
134pub mod map;
135pub mod options;
136pub mod ser;
137pub mod spec;
138pub mod value;
139
140pub use de::Deserializer;
141pub use error::{Error, Result};
142pub use map::ToonMap;
143pub use options::{Delimiter, ToonOptions};
144pub use ser::{Serializer, ValueSerializer};
145pub use value::{Number, Value};
146
147use serde::{Deserialize, Serialize};
148use std::io;
149
150/// Serialize any `T: Serialize` to a TOON string.
151///
152/// # Examples
153///
154/// ```rust
155/// use serde_toon::to_string;
156/// use serde::Serialize;
157///
158/// #[derive(Serialize)]
159/// struct Point { x: i32, y: i32 }
160///
161/// let point = Point { x: 1, y: 2 };
162/// let toon = to_string(&point).unwrap();
163/// ```
164///
165/// # Errors
166///
167/// Returns an error if the value cannot be serialized (e.g., unsupported types).
168#[must_use = "this returns the result of the operation, errors must be handled"]
169pub fn to_string<T>(value: &T) -> Result<String>
170where
171    T: ?Sized + Serialize,
172{
173    to_string_with_options(value, ToonOptions::default())
174}
175
176/// Serialize any `T: Serialize` to a pretty-printed TOON string.
177///
178/// Pretty-printing adds newlines and indentation for readability.
179///
180/// # Examples
181///
182/// ```rust
183/// use serde_toon::to_string_pretty;
184/// use serde::Serialize;
185///
186/// #[derive(Serialize)]
187/// struct Point { x: i32, y: i32 }
188///
189/// let point = Point { x: 1, y: 2 };
190/// let toon = to_string_pretty(&point).unwrap();
191/// ```
192///
193/// # Errors
194///
195/// Returns an error if the value cannot be serialized.
196#[must_use = "this returns the result of the operation, errors must be handled"]
197pub fn to_string_pretty<T>(value: &T) -> Result<String>
198where
199    T: ?Sized + Serialize,
200{
201    to_string_with_options(value, ToonOptions::pretty())
202}
203
204/// Serialize any `T: Serialize` to a TOON string with custom options.
205///
206/// Allows customization of delimiters, indentation, and length markers.
207///
208/// # Examples
209///
210/// ```rust
211/// use serde_toon::{to_string_with_options, ToonOptions, Delimiter};
212/// use serde::Serialize;
213///
214/// #[derive(Serialize)]
215/// struct Point { x: i32, y: i32 }
216///
217/// let point = Point { x: 1, y: 2 };
218/// let options = ToonOptions::new()
219///     .with_delimiter(Delimiter::Tab)
220///     .with_length_marker('#');
221/// let toon = to_string_with_options(&point, options).unwrap();
222/// ```
223///
224/// # Errors
225///
226/// Returns an error if the value cannot be serialized.
227#[must_use = "this returns the result of the operation, errors must be handled"]
228pub fn to_string_with_options<T>(value: &T, options: ToonOptions) -> Result<String>
229where
230    T: ?Sized + Serialize,
231{
232    let mut serializer = Serializer::new(options);
233    value.serialize(&mut serializer)?;
234    Ok(serializer.into_inner())
235}
236
237/// Convert any `T: Serialize` to a `Value`.
238///
239/// Useful for working with TOON data dynamically when the structure isn't known at compile time.
240///
241/// # Examples
242///
243/// ```rust
244/// use serde_toon::{to_value, Value};
245/// use serde::Serialize;
246///
247/// #[derive(Serialize)]
248/// struct Point { x: i32, y: i32 }
249///
250/// let point = Point { x: 1, y: 2 };
251/// let value: Value = to_value(&point).unwrap();
252/// assert!(value.is_object());
253/// ```
254///
255/// # Errors
256///
257/// Returns an error if the value cannot be serialized.
258#[must_use = "this returns the result of the operation, errors must be handled"]
259pub fn to_value<T>(value: &T) -> Result<Value>
260where
261    T: ?Sized + Serialize,
262{
263    value.serialize(crate::ser::ValueSerializer)
264}
265
266/// Serialize any `T: Serialize` to a writer in TOON format.
267///
268/// # Examples
269///
270/// ```rust
271/// use serde_toon::to_writer;
272/// use serde::Serialize;
273/// use std::io::Cursor;
274///
275/// #[derive(Serialize)]
276/// struct Point { x: i32, y: i32 }
277///
278/// let point = Point { x: 1, y: 2 };
279/// let mut buffer = Vec::new();
280/// to_writer(&mut buffer, &point).unwrap();
281/// ```
282///
283/// # Errors
284///
285/// Returns an error if serialization fails or writing to the writer fails.
286#[must_use = "this returns the result of the operation, errors must be handled"]
287pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
288where
289    W: io::Write,
290    T: ?Sized + Serialize,
291{
292    to_writer_with_options(writer, value, ToonOptions::default())
293}
294
295/// Serialize any `T: Serialize` to a writer in TOON format with custom options.
296///
297/// # Errors
298///
299/// Returns an error if serialization fails or writing to the writer fails.
300#[must_use = "this returns the result of the operation, errors must be handled"]
301pub fn to_writer_with_options<W, T>(mut writer: W, value: &T, options: ToonOptions) -> Result<()>
302where
303    W: io::Write,
304    T: ?Sized + Serialize,
305{
306    let toon_string = to_string_with_options(value, options)?;
307    writer
308        .write_all(toon_string.as_bytes())
309        .map_err(|e| Error::io(&e.to_string()))?;
310    Ok(())
311}
312
313/// Deserialize an instance of type `T` from a string of TOON text.
314///
315/// # Examples
316///
317/// ```rust
318/// use serde_toon::from_str;
319/// use serde::Deserialize;
320///
321/// #[derive(Deserialize, PartialEq, Debug)]
322/// struct Point { x: i32, y: i32 }
323///
324/// let toon = "x: 1\ny: 2";
325/// let point: Point = from_str(toon).unwrap();
326/// assert_eq!(point, Point { x: 1, y: 2 });
327/// ```
328///
329/// # Errors
330///
331/// Returns an error if the input is not valid TOON format or cannot be deserialized to type `T`.
332/// Error messages include line and column information.
333#[must_use = "this returns the result of the operation, errors must be handled"]
334pub fn from_str<'a, T>(s: &'a str) -> Result<T>
335where
336    T: Deserialize<'a>,
337{
338    let mut deserializer = Deserializer::from_str(s);
339    T::deserialize(&mut deserializer)
340}
341
342/// Deserialize an instance of type `T` from an I/O stream of TOON.
343///
344/// # Examples
345///
346/// ```rust
347/// use serde_toon::from_reader;
348/// use serde::Deserialize;
349/// use std::io::Cursor;
350///
351/// #[derive(Deserialize, PartialEq, Debug)]
352/// struct Point { x: i32, y: i32 }
353///
354/// let toon_bytes = b"x: 1\ny: 2";
355/// let cursor = Cursor::new(toon_bytes);
356/// let point: Point = from_reader(cursor).unwrap();
357/// assert_eq!(point, Point { x: 1, y: 2 });
358/// ```
359///
360/// # Errors
361///
362/// Returns an error if reading from the reader fails, the input is not valid TOON,
363/// or the data cannot be deserialized to type `T`.
364#[must_use = "this returns the result of the operation, errors must be handled"]
365pub fn from_reader<R, T>(mut reader: R) -> Result<T>
366where
367    R: io::Read,
368    T: for<'de> Deserialize<'de>,
369{
370    let mut string = String::new();
371    reader
372        .read_to_string(&mut string)
373        .map_err(|e| Error::io(&e.to_string()))?;
374    from_str(&string)
375}
376
377/// Deserialize an instance of type `T` from bytes of TOON text.
378///
379/// # Examples
380///
381/// ```rust
382/// use serde_toon::from_slice;
383/// use serde::Deserialize;
384///
385/// #[derive(Deserialize, PartialEq, Debug)]
386/// struct Point { x: i32, y: i32 }
387///
388/// let toon_bytes = b"x: 1\ny: 2";
389/// let point: Point = from_slice(toon_bytes).unwrap();
390/// assert_eq!(point, Point { x: 1, y: 2 });
391/// ```
392///
393/// # Errors
394///
395/// Returns an error if the bytes are not valid UTF-8, not valid TOON format,
396/// or cannot be deserialized to type `T`.
397#[must_use = "this returns the result of the operation, errors must be handled"]
398pub fn from_slice<'a, T>(v: &'a [u8]) -> Result<T>
399where
400    T: Deserialize<'a>,
401{
402    let s = std::str::from_utf8(v).map_err(|e| Error::custom(e.to_string()))?;
403    from_str(s)
404}
405
406#[cfg(test)]
407mod tests {
408    use super::*;
409    use serde::{Deserialize, Serialize};
410
411    #[derive(Serialize, Deserialize, Debug, PartialEq)]
412    struct Point {
413        x: i32,
414        y: i32,
415    }
416
417    #[derive(Serialize, Deserialize, Debug, PartialEq)]
418    struct User {
419        id: u32,
420        name: String,
421        active: bool,
422        tags: Vec<String>,
423    }
424
425    #[test]
426    fn test_serialize_deserialize_point() {
427        let point = Point { x: 1, y: 2 };
428        let toon = to_string(&point).unwrap();
429        let point_back: Point = from_str(&toon).unwrap();
430        assert_eq!(point, point_back);
431    }
432
433    #[test]
434    fn test_serialize_deserialize_user() {
435        let user = User {
436            id: 123,
437            name: "Alice".to_string(),
438            active: true,
439            tags: vec!["admin".to_string(), "user".to_string()],
440        };
441
442        let toon = to_string(&user).unwrap();
443        let user_back: User = from_str(&toon).unwrap();
444        assert_eq!(user, user_back);
445    }
446
447    #[test]
448    fn test_pretty_printing() {
449        let user = User {
450            id: 123,
451            name: "Alice".to_string(),
452            active: true,
453            tags: vec!["admin".to_string(), "user".to_string()],
454        };
455
456        let toon = to_string_pretty(&user).unwrap();
457        let user_back: User = from_str(&toon).unwrap();
458        assert_eq!(user, user_back);
459    }
460
461    #[test]
462    fn test_to_value() {
463        let point = Point { x: 1, y: 2 };
464        let value = to_value(&point).unwrap();
465
466        match value {
467            Value::Object(obj) => {
468                assert_eq!(obj.get("x"), Some(&Value::Number(Number::Integer(1))));
469                assert_eq!(obj.get("y"), Some(&Value::Number(Number::Integer(2))));
470            }
471            _ => panic!("Expected object"),
472        }
473    }
474
475    #[test]
476    fn test_arrays() {
477        let numbers = vec![1, 2, 3, 4, 5];
478        let toon = to_string(&numbers).unwrap();
479        let numbers_back: Vec<i32> = from_str(&toon).unwrap();
480        assert_eq!(numbers, numbers_back);
481    }
482
483    #[test]
484    fn test_custom_options() {
485        let user = User {
486            id: 123,
487            name: "Alice".to_string(),
488            active: true,
489            tags: vec!["admin".to_string(), "user".to_string()],
490        };
491
492        let options = ToonOptions::new()
493            .with_delimiter(Delimiter::Tab)
494            .with_length_marker('#');
495
496        let toon = to_string_with_options(&user, options).unwrap();
497        let user_back: User = from_str(&toon).unwrap();
498        assert_eq!(user, user_back);
499    }
500}