toon_format/
lib.rs

1//! # TOON Format for Rust
2//!
3//! Token-Oriented Object Notation (TOON) is a compact, human-readable format
4//! designed for passing structured data to Large Language Models with
5//! significantly reduced token usage.
6//!
7//! This crate reserves the `toon-format` namespace for the official Rust
8//! implementation. Full implementation coming soon!
9//!
10//! ## Resources
11//!
12//! - [TOON Specification](https://github.com/johannschopplich/toon/blob/main/SPEC.md)
13//! - [Main Repository](https://github.com/johannschopplich/toon)
14//! - [Other Implementations](https://github.com/johannschopplich/toon#other-implementations)
15//!
16//! ## Example Usage (Future)
17//!
18//! ```ignore
19//! use toon_format::{encode, decode};
20//!
21//! let data = json!({"name": "Alice", "age": 30});
22//! let toon_string = encode(&data)?;
23//! let decoded = decode(&toon_string)?;
24//! # Ok::<(), toon_format::ToonError>(())
25//! ```
26#![warn(rustdoc::missing_crate_level_docs)]
27
28pub mod constants;
29pub mod decode;
30pub mod encode;
31pub mod types;
32pub mod utils;
33
34pub use decode::{
35    decode,
36    decode_default,
37    decode_no_coerce,
38    decode_no_coerce_with_options,
39    decode_strict,
40    decode_strict_with_options,
41};
42pub use encode::{
43    encode,
44    encode_array,
45    encode_default,
46    encode_object,
47};
48pub use types::{
49    DecodeOptions,
50    Delimiter,
51    EncodeOptions,
52    ToonError,
53};
54pub use utils::{
55    literal::{
56        is_keyword,
57        is_literal_like,
58    },
59    normalize,
60    string::{
61        escape_string,
62        is_valid_unquoted_key,
63        needs_quoting,
64    },
65};
66
67#[cfg(test)]
68mod tests {
69    use serde_json::json;
70
71    use crate::{
72        constants::is_keyword,
73        decode::{
74            decode_default,
75            decode_strict,
76        },
77        encode::{
78            encode,
79            encode_default,
80        },
81        types::{
82            Delimiter,
83            EncodeOptions,
84        },
85        utils::{
86            escape_string,
87            is_literal_like,
88            needs_quoting,
89            normalize,
90        },
91    };
92
93    #[test]
94    fn test_round_trip_simple() {
95        let original = json!({"name": "Alice", "age": 30});
96        let encoded = encode_default(&original).unwrap();
97        let decoded = decode_default(&encoded).unwrap();
98        assert_eq!(original, decoded);
99    }
100
101    #[test]
102    fn test_round_trip_array() {
103        let original = json!({"tags": ["reading", "gaming", "coding"]});
104        let encoded = encode_default(&original).unwrap();
105        let decoded = decode_default(&encoded).unwrap();
106        assert_eq!(original, decoded);
107    }
108
109    #[test]
110    fn test_round_trip_tabular() {
111        let original = json!({
112            "users": [
113                {"id": 1, "name": "Alice", "role": "admin"},
114                {"id": 2, "name": "Bob", "role": "user"}
115            ]
116        });
117        let encoded = encode_default(&original).unwrap();
118        let decoded = decode_default(&encoded).unwrap();
119        assert_eq!(original, decoded);
120    }
121
122    #[test]
123    fn test_custom_delimiter() {
124        let original = json!({"tags": ["a", "b", "c"]});
125        let opts = EncodeOptions::new().with_delimiter(Delimiter::Pipe);
126        let encoded = encode(&original, &opts).unwrap();
127        assert!(encoded.contains("|"));
128
129        let decoded = decode_default(&encoded).unwrap();
130        assert_eq!(original, decoded);
131    }
132
133    #[test]
134    fn test_length_marker() {
135        let original = json!({"tags": ["a", "b", "c"]});
136        let opts = EncodeOptions::new().with_length_marker('#');
137        let encoded = encode(&original, &opts).unwrap();
138        assert!(encoded.contains("[#3]"));
139
140        let decoded = decode_default(&encoded).unwrap();
141        assert_eq!(original, decoded);
142    }
143
144    #[test]
145    fn test_decode_strict_helper() {
146        let input = "items[2]: a,b";
147        assert!(decode_strict(input).is_ok());
148
149        let input = "items[3]: a,b";
150        assert!(decode_strict(input).is_err());
151    }
152
153    #[test]
154    fn test_normalize_exported() {
155        let value = json!(f64::NAN);
156        let normalized = normalize(value.into());
157        assert_eq!(serde_json::Value::from(normalized), json!(null));
158    }
159
160    #[test]
161    fn test_utilities_exported() {
162        assert!(is_keyword("null"));
163        assert!(is_literal_like("true"));
164        assert_eq!(escape_string("hello\nworld"), "hello\\nworld");
165        assert!(needs_quoting("true", Delimiter::Comma));
166    }
167}