toon_format/decode/mod.rs
1//! Decoder Implementation
2pub mod parser;
3pub mod scanner;
4pub mod validation;
5
6use serde_json::Value;
7
8use crate::types::{
9 DecodeOptions,
10 ToonResult,
11};
12
13/// Decode a TOON string to a JSON value with custom options.
14///
15/// # Examples
16///
17/// ```
18/// use serde_json::json;
19/// use toon_format::{
20/// decode,
21/// DecodeOptions,
22/// Delimiter,
23/// };
24///
25/// let input = "name: Alice\nage: 30";
26/// let options = DecodeOptions::new().with_strict(false);
27/// let result = decode(input, &options)?;
28/// assert_eq!(result["name"], json!("Alice"));
29/// # Ok::<(), toon_format::ToonError>(())
30/// ```
31pub fn decode(input: &str, options: &DecodeOptions) -> ToonResult<Value> {
32 let mut parser = parser::Parser::new(input, options.clone());
33 parser.parse()
34}
35
36/// Decode with strict validation enabled (validates array lengths,
37/// indentation).
38///
39/// # Examples
40///
41/// ```
42/// use serde_json::json;
43/// use toon_format::decode_strict;
44///
45/// // Valid array length
46/// let result = decode_strict("items[2]: a,b")?;
47/// assert_eq!(result["items"], json!(["a", "b"]));
48///
49/// // Invalid array length (will error)
50/// assert!(decode_strict("items[3]: a,b").is_err());
51/// # Ok::<(), toon_format::ToonError>(())
52/// ```
53pub fn decode_strict(input: &str) -> ToonResult<Value> {
54 decode(input, &DecodeOptions::new().with_strict(true))
55}
56
57/// Decode with strict validation and additional options.
58///
59/// # Examples
60///
61/// ```
62/// use serde_json::json;
63/// use toon_format::{
64/// decode_strict_with_options,
65/// DecodeOptions,
66/// };
67///
68/// let options = DecodeOptions::new()
69/// .with_strict(true)
70/// .with_delimiter(toon_format::Delimiter::Pipe);
71/// let result = decode_strict_with_options("items[2]: a|b", &options)?;
72/// assert_eq!(result["items"], json!(["a", "b"]));
73/// # Ok::<(), toon_format::ToonError>(())
74/// ```
75pub fn decode_strict_with_options(input: &str, options: &DecodeOptions) -> ToonResult<Value> {
76 let opts = options.clone().with_strict(true);
77 decode(input, &opts)
78}
79
80/// Decode without type coercion (strings remain strings).
81///
82/// # Examples
83///
84/// ```
85/// use serde_json::json;
86/// use toon_format::decode_no_coerce;
87///
88/// // Without coercion: quoted strings that look like numbers stay as strings
89/// let result = decode_no_coerce("value: \"123\"")?;
90/// assert_eq!(result["value"], json!("123"));
91///
92/// // With default coercion: unquoted "true" becomes boolean
93/// let result = toon_format::decode_default("value: true")?;
94/// assert_eq!(result["value"], json!(true));
95/// # Ok::<(), toon_format::ToonError>(())
96/// ```
97pub fn decode_no_coerce(input: &str) -> ToonResult<Value> {
98 decode(input, &DecodeOptions::new().with_coerce_types(false))
99}
100
101/// Decode without type coercion and with additional options.
102///
103/// # Examples
104///
105/// ```
106/// use serde_json::json;
107/// use toon_format::{
108/// decode_no_coerce_with_options,
109/// DecodeOptions,
110/// };
111///
112/// let options = DecodeOptions::new()
113/// .with_coerce_types(false)
114/// .with_strict(false);
115/// let result = decode_no_coerce_with_options("value: \"123\"", &options)?;
116/// assert_eq!(result["value"], json!("123"));
117/// # Ok::<(), toon_format::ToonError>(())
118/// ```
119pub fn decode_no_coerce_with_options(input: &str, options: &DecodeOptions) -> ToonResult<Value> {
120 let opts = options.clone().with_coerce_types(false);
121 decode(input, &opts)
122}
123
124/// Decode with default options (strict mode, type coercion enabled).
125///
126/// # Examples
127///
128/// ```
129/// use serde_json::json;
130/// use toon_format::decode_default;
131///
132/// // Simple object
133/// let input = "name: Alice\nage: 30";
134/// let result = decode_default(input)?;
135/// assert_eq!(result["name"], json!("Alice"));
136/// assert_eq!(result["age"], json!(30));
137///
138/// // Primitive array
139/// let input = "tags[3]: reading,gaming,coding";
140/// let result = decode_default(input)?;
141/// assert_eq!(result["tags"], json!(["reading", "gaming", "coding"]));
142///
143/// // Tabular array
144/// let input = "users[2]{id,name}:\n 1,Alice\n 2,Bob";
145/// let result = decode_default(input)?;
146/// assert_eq!(result["users"][0]["name"], json!("Alice"));
147/// # Ok::<(), toon_format::ToonError>(())
148/// ```
149pub fn decode_default(input: &str) -> ToonResult<Value> {
150 decode(input, &DecodeOptions::default())
151}
152
153#[cfg(test)]
154mod tests {
155 use core::f64;
156
157 use serde_json::json;
158
159 use super::*;
160
161 #[test]
162 fn test_decode_null() {
163 assert_eq!(decode_default("null").unwrap(), json!(null));
164 }
165
166 #[test]
167 fn test_decode_bool() {
168 assert_eq!(decode_default("true").unwrap(), json!(true));
169 assert_eq!(decode_default("false").unwrap(), json!(false));
170 }
171
172 #[test]
173 fn test_decode_number() {
174 assert_eq!(decode_default("42").unwrap(), json!(42));
175 assert_eq!(
176 decode_default("3.141592653589793").unwrap(),
177 json!(f64::consts::PI)
178 );
179 assert_eq!(decode_default("-5").unwrap(), json!(-5));
180 }
181
182 #[test]
183 fn test_decode_string() {
184 assert_eq!(decode_default("hello").unwrap(), json!("hello"));
185 assert_eq!(
186 decode_default("\"hello world\"").unwrap(),
187 json!("hello world")
188 );
189 }
190
191 #[test]
192 fn test_decode_simple_object() {
193 let input = "name: Alice\nage: 30";
194 let result = decode_default(input).unwrap();
195 assert_eq!(result["name"], json!("Alice"));
196 assert_eq!(result["age"], json!(30));
197 }
198
199 #[test]
200 fn test_decode_primitive_array() {
201 let input = "tags[3]: reading,gaming,coding";
202 let result = decode_default(input).unwrap();
203 assert_eq!(result["tags"], json!(["reading", "gaming", "coding"]));
204 }
205
206 #[test]
207 fn test_decode_tabular_array() {
208 let input = "users[2]{id,name,role}:\n 1,Alice,admin\n 2,Bob,user";
209 let result = decode_default(input).unwrap();
210 assert_eq!(
211 result["users"],
212 json!([
213 {"id": 1, "name": "Alice", "role": "admin"},
214 {"id": 2, "name": "Bob", "role": "user"}
215 ])
216 );
217 }
218
219 #[test]
220 fn test_decode_empty_array() {
221 let input = "items[0]:";
222 let result = decode_default(input).unwrap();
223 assert_eq!(result["items"], json!([]));
224 }
225
226 #[test]
227 fn test_decode_quoted_strings() {
228 let input = "tags[3]: \"true\",\"42\",\"-3.14\"";
229 let result = decode_default(input).unwrap();
230 assert_eq!(result["tags"], json!(["true", "42", "-3.14"]));
231 }
232}