toon_macro/
lib.rs

1//! # toon-macro
2//!
3//! Ergonomic macros for constructing and parsing TOON (Token-Oriented Object Notation) values.
4//!
5//! TOON is a compact data format designed to convey the same information as JSON
6//! with 30-60% fewer tokens, making it ideal for LLM prompts and responses.
7//!
8//! ## Features
9//!
10//! - **`toon!` macro**: JSON-like Rust DSL for constructing TOON values
11//! - **`toon_str!` macro**: Parse TOON-format strings at runtime
12//! - **`ToonTable` trait**: Encode/decode tabular data efficiently
13//! - **`#[derive(ToonTable)]`**: Automatic table serialization (requires `derive` feature)
14//!
15//! ## Quick Start
16//!
17//! ### Using `toon!` (Rust-DSL)
18//!
19//! The `toon!` macro provides a JSON-like syntax for constructing TOON values:
20//!
21//! ```
22//! use toon_macro::{toon, Value};
23//!
24//! // Simple object
25//! let user = toon!({
26//!     name: "Alice",
27//!     age: 30,
28//!     active: true
29//! });
30//!
31//! // Nested structures
32//! let data = toon!({
33//!     config: {
34//!         host: "localhost",
35//!         port: 8080
36//!     },
37//!     users: [
38//!         { id: 1, name: "Alice" },
39//!         { id: 2, name: "Bob" }
40//!     ]
41//! });
42//!
43//! // Using variables
44//! let name = "Charlie";
45//! let score = 95i64;
46//! let result = toon!({
47//!     name: name,
48//!     score: score
49//! });
50//! ```
51//!
52//! ### Using `toon_str!` (TOON syntax)
53//!
54//! The `toon_str!` macro parses TOON-format text at runtime:
55//!
56//! ```
57//! use toon_macro::toon_str;
58//!
59//! let config = toon_str!(r#"
60//! host: "localhost"
61//! port: 5432
62//! active: true
63//! "#);
64//! ```
65//!
66//! ### Using `from_toon_str` (with error handling)
67//!
68//! For fallible parsing, use `from_toon_str` directly:
69//!
70//! ```
71//! use toon_macro::from_toon_str;
72//!
73//! let input = r#"name: "Alice""#;
74//! match from_toon_str(input) {
75//!     Ok(value) => println!("Parsed: {:?}", value),
76//!     Err(e) => eprintln!("Parse error: {}", e),
77//! }
78//! ```
79//!
80//! ### Using `ToonTable` (requires `derive` feature)
81//!
82//! ```ignore
83//! use toon_macro::ToonTable;
84//!
85//! #[derive(ToonTable)]
86//! struct User {
87//!     id: u64,
88//!     name: String,
89//!     #[toon(rename = "user_role")]
90//!     role: String,
91//! }
92//!
93//! let users = vec![
94//!     User { id: 1, name: "Alice".into(), role: "admin".into() },
95//!     User { id: 2, name: "Bob".into(), role: "user".into() },
96//! ];
97//!
98//! // Encode to compact table format
99//! let table = User::to_toon_table(&users);
100//!
101//! // Decode back to structs
102//! let decoded = User::from_toon_table(&table).unwrap();
103//! ```
104//!
105//! ## Feature Flags
106//!
107//! - `serde` (default): Enable serde integration for serializing arbitrary types
108//! - `derive`: Enable `#[derive(ToonTable)]` macro
109//! - `pretty`: Enable pretty-printing functions
110//!
111//! ## Minimum Supported Rust Version
112//!
113//! This crate requires Rust 1.70 or later.
114
115#![warn(missing_docs)]
116#![warn(clippy::all)]
117#![deny(unsafe_code)]
118
119// Declare modules - macros must come first so they can be used in other modules
120#[macro_use]
121pub mod internal;
122#[macro_use]
123pub mod macros;
124
125pub mod error;
126pub mod ser;
127pub mod table;
128pub mod value;
129
130// Re-export core types
131pub use error::{Error, Result};
132pub use ser::{from_toon_str, to_toon_string};
133pub use value::Value;
134
135// Re-export the ToonTable trait (always available)
136// When the derive feature is enabled, the derive macro is also re-exported
137// with the same name, which is the standard pattern for derive macros.
138#[cfg(not(feature = "derive"))]
139pub use table::ToonTable;
140
141#[cfg(feature = "derive")]
142pub use table::ToonTable;
143
144// Conditionally re-export derive macro
145#[cfg(feature = "derive")]
146pub use toon_macro_derive::ToonTable;
147
148// Re-export serde_toon2 types that users might need
149pub use serde_toon2::{Map, Number};
150
151// Re-export pretty printing if enabled
152#[cfg(feature = "pretty")]
153pub use ser::to_toon_string_pretty;
154
155// Re-export serde helpers if serde feature is enabled
156#[cfg(feature = "serde")]
157pub use ser::{deserialize, serialize};
158
159#[cfg(feature = "serde")]
160pub use value::{from_value, to_value};
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_toon_macro_basic() {
168        let v = toon!({
169            name: "test",
170            value: 42
171        });
172
173        assert!(matches!(v, Value::Object(_)));
174    }
175
176    #[test]
177    fn test_toon_str_basic() {
178        let v = toon_str!(r#"name: "test""#);
179        assert!(matches!(v, Value::Object(_)));
180    }
181
182    #[test]
183    fn test_from_toon_str_error_handling() {
184        // Test that we can handle errors gracefully
185        let result = from_toon_str("name: \"valid\"");
186        assert!(result.is_ok());
187    }
188
189    #[test]
190    fn test_to_toon_string() {
191        let v = toon!({ key: "value" });
192        let s = to_toon_string(&v).unwrap();
193        assert!(s.contains("key"));
194        assert!(s.contains("value"));
195    }
196
197    #[cfg(feature = "serde")]
198    #[test]
199    fn test_serde_roundtrip() {
200        use serde::{Deserialize, Serialize};
201
202        #[derive(Serialize, Deserialize, Debug, PartialEq)]
203        struct Point {
204            x: i64,
205            y: i64,
206        }
207
208        let point = Point { x: 10, y: 20 };
209        let serialized = serialize(&point).unwrap();
210        let deserialized: Point = deserialize(&serialized).unwrap();
211        assert_eq!(point, deserialized);
212    }
213}