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}