toon_format/decode/
mod.rs

1//! Decoder Implementation
2pub mod expansion;
3pub mod parser;
4pub mod scanner;
5pub mod validation;
6
7use serde_json::Value;
8
9use crate::types::{
10    DecodeOptions,
11    ToonResult,
12};
13
14/// Decode a TOON string into any deserializable type.
15///
16/// This function accepts any type implementing `serde::Deserialize`, including:
17/// - Custom structs with `#[derive(Deserialize)]`
18/// - `serde_json::Value`
19/// - Built-in types (Vec, HashMap, etc.)
20///
21/// # Examples
22///
23/// **With custom structs:**
24/// ```
25/// use serde::Deserialize;
26/// use toon_format::{
27///     decode,
28///     DecodeOptions,
29/// };
30///
31/// #[derive(Deserialize, Debug, PartialEq)]
32/// struct User {
33///     name: String,
34///     age: u32,
35/// }
36///
37/// let toon = "name: Alice\nage: 30";
38/// let user: User = decode(toon, &DecodeOptions::default())?;
39/// assert_eq!(user.name, "Alice");
40/// assert_eq!(user.age, 30);
41/// # Ok::<(), toon_format::ToonError>(())
42/// ```
43///
44/// **With JSON values:**
45/// ```
46/// use serde_json::{
47///     json,
48///     Value,
49/// };
50/// use toon_format::{
51///     decode,
52///     DecodeOptions,
53/// };
54///
55/// let input = "name: Alice\nage: 30";
56/// let result: Value = decode(input, &DecodeOptions::default())?;
57/// assert_eq!(result["name"], json!("Alice"));
58/// # Ok::<(), toon_format::ToonError>(())
59/// ```
60pub fn decode<T: serde::de::DeserializeOwned>(
61    input: &str,
62    options: &DecodeOptions,
63) -> ToonResult<T> {
64    let mut parser = parser::Parser::new(input, options.clone())?;
65    let value = parser.parse()?;
66
67    // Apply path expansion if enabled (v1.5 feature)
68    use crate::types::PathExpansionMode;
69    let final_value = if options.expand_paths != PathExpansionMode::Off {
70        let json_value = crate::types::JsonValue::from(value);
71        let expanded =
72            expansion::expand_paths_recursive(json_value, options.expand_paths, options.strict)?;
73        Value::from(expanded)
74    } else {
75        value
76    };
77
78    serde_json::from_value(final_value)
79        .map_err(|e| crate::types::ToonError::DeserializationError(e.to_string()))
80}
81
82/// Decode with strict validation enabled (validates array lengths,
83/// indentation).
84///
85/// # Examples
86///
87/// ```
88/// use serde_json::{
89///     json,
90///     Value,
91/// };
92/// use toon_format::decode_strict;
93///
94/// // Valid array length
95/// let result: Value = decode_strict("items[2]: a,b")?;
96/// assert_eq!(result["items"], json!(["a", "b"]));
97///
98/// // Invalid array length (will error)
99/// assert!(decode_strict::<Value>("items[3]: a,b").is_err());
100/// # Ok::<(), toon_format::ToonError>(())
101/// ```
102pub fn decode_strict<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
103    decode(input, &DecodeOptions::new().with_strict(true))
104}
105
106/// Decode with strict validation and additional options.
107///
108/// # Examples
109///
110/// ```
111/// use serde_json::{
112///     json,
113///     Value,
114/// };
115/// use toon_format::{
116///     decode_strict_with_options,
117///     DecodeOptions,
118/// };
119///
120/// let options = DecodeOptions::new()
121///     .with_strict(true)
122///     .with_delimiter(toon_format::Delimiter::Pipe);
123/// let result: Value = decode_strict_with_options("items[2|]: a|b", &options)?;
124/// assert_eq!(result["items"], json!(["a", "b"]));
125/// # Ok::<(), toon_format::ToonError>(())
126/// ```
127pub fn decode_strict_with_options<T: serde::de::DeserializeOwned>(
128    input: &str,
129    options: &DecodeOptions,
130) -> ToonResult<T> {
131    let opts = options.clone().with_strict(true);
132    decode(input, &opts)
133}
134
135/// Decode without type coercion (strings remain strings).
136///
137/// # Examples
138///
139/// ```
140/// use serde_json::{
141///     json,
142///     Value,
143/// };
144/// use toon_format::decode_no_coerce;
145///
146/// // Without coercion: quoted strings that look like numbers stay as strings
147/// let result: Value = decode_no_coerce("value: \"123\"")?;
148/// assert_eq!(result["value"], json!("123"));
149///
150/// // With default coercion: unquoted "true" becomes boolean
151/// let result: Value = toon_format::decode_default("value: true")?;
152/// assert_eq!(result["value"], json!(true));
153/// # Ok::<(), toon_format::ToonError>(())
154/// ```
155pub fn decode_no_coerce<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
156    decode(input, &DecodeOptions::new().with_coerce_types(false))
157}
158
159/// Decode without type coercion and with additional options.
160///
161/// # Examples
162///
163/// ```
164/// use serde_json::{
165///     json,
166///     Value,
167/// };
168/// use toon_format::{
169///     decode_no_coerce_with_options,
170///     DecodeOptions,
171/// };
172///
173/// let options = DecodeOptions::new()
174///     .with_coerce_types(false)
175///     .with_strict(false);
176/// let result: Value = decode_no_coerce_with_options("value: \"123\"", &options)?;
177/// assert_eq!(result["value"], json!("123"));
178/// # Ok::<(), toon_format::ToonError>(())
179/// ```
180pub fn decode_no_coerce_with_options<T: serde::de::DeserializeOwned>(
181    input: &str,
182    options: &DecodeOptions,
183) -> ToonResult<T> {
184    let opts = options.clone().with_coerce_types(false);
185    decode(input, &opts)
186}
187
188/// Decode with default options (strict mode, type coercion enabled).
189///
190/// Works with any type implementing `serde::Deserialize`.
191///
192/// # Examples
193///
194/// **With structs:**
195/// ```
196/// use serde::Deserialize;
197/// use toon_format::decode_default;
198///
199/// #[derive(Deserialize)]
200/// struct Person {
201///     name: String,
202///     age: u32,
203/// }
204///
205/// let input = "name: Alice\nage: 30";
206/// let person: Person = decode_default(input)?;
207/// assert_eq!(person.name, "Alice");
208/// # Ok::<(), toon_format::ToonError>(())
209/// ```
210///
211/// **With JSON values:**
212/// ```
213/// use serde_json::{
214///     json,
215///     Value,
216/// };
217/// use toon_format::decode_default;
218///
219/// let input = "tags[3]: reading,gaming,coding";
220/// let result: Value = decode_default(input)?;
221/// assert_eq!(result["tags"], json!(["reading", "gaming", "coding"]));
222/// # Ok::<(), toon_format::ToonError>(())
223/// ```
224pub fn decode_default<T: serde::de::DeserializeOwned>(input: &str) -> ToonResult<T> {
225    decode(input, &DecodeOptions::default())
226}
227
228#[cfg(test)]
229mod tests {
230    use core::f64;
231
232    use serde_json::json;
233
234    use super::*;
235
236    #[test]
237    fn test_decode_null() {
238        assert_eq!(decode_default::<Value>("null").unwrap(), json!(null));
239    }
240
241    #[test]
242    fn test_decode_bool() {
243        assert_eq!(decode_default::<Value>("true").unwrap(), json!(true));
244        assert_eq!(decode_default::<Value>("false").unwrap(), json!(false));
245    }
246
247    #[test]
248    fn test_decode_number() {
249        assert_eq!(decode_default::<Value>("42").unwrap(), json!(42));
250        assert_eq!(
251            decode_default::<Value>("3.141592653589793").unwrap(),
252            json!(f64::consts::PI)
253        );
254        assert_eq!(decode_default::<Value>("-5").unwrap(), json!(-5));
255    }
256
257    #[test]
258    fn test_decode_string() {
259        assert_eq!(decode_default::<Value>("hello").unwrap(), json!("hello"));
260        assert_eq!(
261            decode_default::<Value>("\"hello world\"").unwrap(),
262            json!("hello world")
263        );
264    }
265
266    #[test]
267    fn test_decode_simple_object() {
268        let input = "name: Alice\nage: 30";
269        let result: Value = decode_default(input).unwrap();
270        assert_eq!(result["name"], json!("Alice"));
271        assert_eq!(result["age"], json!(30));
272    }
273
274    #[test]
275    fn test_decode_primitive_array() {
276        let input = "tags[3]: reading,gaming,coding";
277        let result: Value = decode_default(input).unwrap();
278        assert_eq!(result["tags"], json!(["reading", "gaming", "coding"]));
279    }
280
281    #[test]
282    fn test_decode_tabular_array() {
283        let input = "users[2]{id,name,role}:\n  1,Alice,admin\n  2,Bob,user";
284        let result: Value = decode_default(input).unwrap();
285        assert_eq!(
286            result["users"],
287            json!([
288                {"id": 1, "name": "Alice", "role": "admin"},
289                {"id": 2, "name": "Bob", "role": "user"}
290            ])
291        );
292    }
293
294    #[test]
295    fn test_decode_empty_array() {
296        let input = "items[0]:";
297        let result: Value = decode_default(input).unwrap();
298        assert_eq!(result["items"], json!([]));
299    }
300
301    #[test]
302    fn test_decode_quoted_strings() {
303        let input = "tags[3]: \"true\",\"42\",\"-3.14\"";
304        let result: Value = decode_default(input).unwrap();
305        assert_eq!(result["tags"], json!(["true", "42", "-3.14"]));
306    }
307}