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    Indent,
53    ToonError,
54};
55pub use utils::{
56    literal::{
57        is_keyword,
58        is_literal_like,
59    },
60    normalize,
61    string::{
62        escape_string,
63        is_valid_unquoted_key,
64        needs_quoting,
65    },
66};
67
68#[cfg(test)]
69mod tests {
70    use serde_json::json;
71
72    use crate::{
73        constants::is_keyword,
74        decode::{
75            decode_default,
76            decode_strict,
77        },
78        encode::{
79            encode,
80            encode_default,
81        },
82        types::{
83            Delimiter,
84            EncodeOptions,
85        },
86        utils::{
87            escape_string,
88            is_literal_like,
89            needs_quoting,
90            normalize,
91        },
92    };
93
94    #[test]
95    fn test_round_trip_simple() {
96        let original = json!({"name": "Alice", "age": 30});
97        let encoded = encode_default(&original).unwrap();
98        let decoded = decode_default(&encoded).unwrap();
99        assert_eq!(original, decoded);
100    }
101
102    #[test]
103    fn test_round_trip_array() {
104        let original = json!({"tags": ["reading", "gaming", "coding"]});
105        let encoded = encode_default(&original).unwrap();
106        let decoded = decode_default(&encoded).unwrap();
107        assert_eq!(original, decoded);
108    }
109
110    #[test]
111    fn test_round_trip_tabular() {
112        let original = json!({
113            "users": [
114                {"id": 1, "name": "Alice", "role": "admin"},
115                {"id": 2, "name": "Bob", "role": "user"}
116            ]
117        });
118        let encoded = encode_default(&original).unwrap();
119        let decoded = decode_default(&encoded).unwrap();
120        assert_eq!(original, decoded);
121    }
122
123    #[test]
124    fn test_custom_delimiter() {
125        let original = json!({"tags": ["a", "b", "c"]});
126        let opts = EncodeOptions::new().with_delimiter(Delimiter::Pipe);
127        let encoded = encode(&original, &opts).unwrap();
128        assert!(encoded.contains("|"));
129
130        let decoded = decode_default(&encoded).unwrap();
131        assert_eq!(original, decoded);
132    }
133
134    #[test]
135    fn test_length_marker() {
136        let original = json!({"tags": ["a", "b", "c"]});
137        let opts = EncodeOptions::new().with_length_marker('#');
138        let encoded = encode(&original, &opts).unwrap();
139        assert!(encoded.contains("[#3]"));
140
141        let decoded = decode_default(&encoded).unwrap();
142        assert_eq!(original, decoded);
143    }
144
145    #[test]
146    fn test_decode_strict_helper() {
147        let input = "items[2]: a,b";
148        assert!(decode_strict(input).is_ok());
149
150        let input = "items[3]: a,b";
151        assert!(decode_strict(input).is_err());
152    }
153
154    #[test]
155    fn test_normalize_exported() {
156        let value = json!(f64::NAN);
157        let normalized = normalize(value.into());
158        assert_eq!(serde_json::Value::from(normalized), json!(null));
159    }
160
161    #[test]
162    fn test_utilities_exported() {
163        assert!(is_keyword("null"));
164        assert!(is_literal_like("true"));
165        assert_eq!(escape_string("hello\nworld"), "hello\\nworld");
166        assert!(needs_quoting("true", Delimiter::Comma.as_char()));
167    }
168}