1#![warn(rustdoc::missing_crate_level_docs)]
27
28pub mod constants;
29pub mod decode;
30pub mod encode;
31#[cfg(feature = "cli")]
32pub mod tui;
33pub mod types;
34pub mod utils;
35
36pub use decode::{
37 decode,
38 decode_default,
39 decode_no_coerce,
40 decode_no_coerce_with_options,
41 decode_strict,
42 decode_strict_with_options,
43};
44pub use encode::{
45 encode,
46 encode_array,
47 encode_default,
48 encode_object,
49};
50pub use types::{
51 DecodeOptions,
52 Delimiter,
53 EncodeOptions,
54 Indent,
55 ToonError,
56};
57pub use utils::{
58 literal::{
59 is_keyword,
60 is_literal_like,
61 },
62 normalize,
63 string::{
64 escape_string,
65 is_valid_unquoted_key,
66 needs_quoting,
67 },
68};
69
70#[cfg(test)]
71mod tests {
72 use serde_json::{
73 json,
74 Value,
75 };
76
77 use crate::{
78 constants::is_keyword,
79 decode::{
80 decode_default,
81 decode_strict,
82 },
83 encode::{
84 encode,
85 encode_default,
86 },
87 types::{
88 Delimiter,
89 EncodeOptions,
90 },
91 utils::{
92 escape_string,
93 is_literal_like,
94 needs_quoting,
95 normalize,
96 },
97 };
98
99 #[test]
100 fn test_round_trip_simple() {
101 let original = json!({"name": "Alice", "age": 30});
102 let encoded = encode_default(&original).unwrap();
103 let decoded: Value = decode_default(&encoded).unwrap();
104 assert_eq!(original, decoded);
105 }
106
107 #[test]
108 fn test_round_trip_array() {
109 let original = json!({"tags": ["reading", "gaming", "coding"]});
110 let encoded = encode_default(&original).unwrap();
111 let decoded: Value = decode_default(&encoded).unwrap();
112 assert_eq!(original, decoded);
113 }
114
115 #[test]
116 fn test_round_trip_tabular() {
117 let original = json!({
118 "users": [
119 {"id": 1, "name": "Alice", "role": "admin"},
120 {"id": 2, "name": "Bob", "role": "user"}
121 ]
122 });
123 let encoded = encode_default(&original).unwrap();
124 let decoded: Value = decode_default(&encoded).unwrap();
125 assert_eq!(original, decoded);
126 }
127
128 #[test]
129 fn test_custom_delimiter() {
130 let original = json!({"tags": ["a", "b", "c"]});
131 let opts = EncodeOptions::new().with_delimiter(Delimiter::Pipe);
132 let encoded = encode(&original, &opts).unwrap();
133 assert!(encoded.contains("|"));
134
135 let decoded: Value = decode_default(&encoded).unwrap();
136 assert_eq!(original, decoded);
137 }
138
139 #[test]
140 fn test_decode_strict_helper() {
141 let input = "items[2]: a,b";
142 assert!(decode_strict::<Value>(input).is_ok());
143
144 let input = "items[3]: a,b";
145 assert!(decode_strict::<Value>(input).is_err());
146 }
147
148 #[test]
149 fn test_normalize_exported() {
150 let value = json!(f64::NAN);
151 let normalized = normalize(value.into());
152 assert_eq!(serde_json::Value::from(normalized), json!(null));
153 }
154
155 #[test]
156 fn test_utilities_exported() {
157 assert!(is_keyword("null"));
158 assert!(is_literal_like("true"));
159 assert_eq!(escape_string("hello\nworld"), "hello\\nworld");
160 assert!(needs_quoting("true", Delimiter::Comma.as_char()));
161 }
162
163 use serde::{
164 Deserialize,
165 Serialize,
166 };
167
168 #[derive(Debug, Serialize, Deserialize, PartialEq)]
169 struct TestUser {
170 name: String,
171 age: u32,
172 active: bool,
173 }
174
175 #[test]
176 fn test_encode_decode_simple_struct() {
177 use crate::{
178 decode_default,
179 encode_default,
180 };
181
182 let user = TestUser {
183 name: "Alice".to_string(),
184 age: 30,
185 active: true,
186 };
187
188 let toon = encode_default(&user).unwrap();
189 assert!(toon.contains("name: Alice"));
190 assert!(toon.contains("age: 30"));
191 assert!(toon.contains("active: true"));
192
193 let decoded: TestUser = decode_default(&toon).unwrap();
194 assert_eq!(user, decoded);
195 }
196
197 #[derive(Debug, Serialize, Deserialize, PartialEq)]
198 struct TestProduct {
199 id: u64,
200 name: String,
201 tags: Vec<String>,
202 }
203
204 #[test]
205 fn test_encode_decode_with_array() {
206 use crate::{
207 decode_default,
208 encode_default,
209 };
210
211 let product = TestProduct {
212 id: 42,
213 name: "Widget".to_string(),
214 tags: vec!["electronics".to_string(), "gadgets".to_string()],
215 };
216
217 let toon = encode_default(&product).unwrap();
218 let decoded: TestProduct = decode_default(&toon).unwrap();
219 assert_eq!(product, decoded);
220 }
221
222 #[test]
223 fn test_encode_decode_vec_of_structs() {
224 use crate::{
225 decode_default,
226 encode_default,
227 };
228
229 let users = vec![
230 TestUser {
231 name: "Alice".to_string(),
232 age: 30,
233 active: true,
234 },
235 TestUser {
236 name: "Bob".to_string(),
237 age: 25,
238 active: false,
239 },
240 ];
241
242 let toon = encode_default(&users).unwrap();
243 let decoded: Vec<TestUser> = decode_default(&toon).unwrap();
244 assert_eq!(users, decoded);
245 }
246
247 #[derive(Debug, Serialize, Deserialize, PartialEq)]
248 struct Nested {
249 outer: OuterStruct,
250 }
251
252 #[derive(Debug, Serialize, Deserialize, PartialEq)]
253 struct OuterStruct {
254 inner: InnerStruct,
255 value: i32,
256 }
257
258 #[derive(Debug, Serialize, Deserialize, PartialEq)]
259 struct InnerStruct {
260 data: String,
261 }
262
263 #[test]
264 fn test_encode_decode_nested_structs() {
265 use crate::{
266 decode_default,
267 encode_default,
268 };
269
270 let nested = Nested {
271 outer: OuterStruct {
272 inner: InnerStruct {
273 data: "test".to_string(),
274 },
275 value: 42,
276 },
277 };
278
279 let toon = encode_default(&nested).unwrap();
280 let decoded: Nested = decode_default(&toon).unwrap();
281 assert_eq!(nested, decoded);
282 }
283
284 #[test]
285 fn test_round_trip_list_item_tabular_v3() {
286 use crate::{
287 decode_default,
288 encode_default,
289 };
290
291 let original = json!({
292 "items": [
293 {
294 "users": [
295 {"id": 1, "name": "Alice", "role": "admin"},
296 {"id": 2, "name": "Bob", "role": "user"}
297 ],
298 "status": "active",
299 "count": 2
300 }
301 ]
302 });
303
304 let encoded = encode_default(&original).unwrap();
305 let decoded: Value = decode_default(&encoded).unwrap();
306
307 assert_eq!(original, decoded);
308 }
309
310 #[test]
311 fn test_round_trip_complex_list_item_tabular_v3() {
312 use crate::{
313 decode_default,
314 encode_default,
315 };
316
317 let original = json!({
318 "data": [
319 {
320 "records": [
321 {"id": 1, "value": "x", "score": 100},
322 {"id": 2, "value": "y", "score": 200}
323 ],
324 "total": 2,
325 "status": "active"
326 },
327 {
328 "records": [
329 {"id": 3, "value": "z", "score": 300}
330 ],
331 "total": 1,
332 "status": "pending"
333 }
334 ]
335 });
336
337 let encoded = encode_default(&original).unwrap();
338 let decoded: Value = decode_default(&encoded).unwrap();
339
340 assert_eq!(original, decoded);
341 }
342
343 #[test]
344 fn test_round_trip_mixed_list_items_v3() {
345 use crate::{
346 decode_default,
347 encode_default,
348 };
349
350 let original = json!({
351 "entries": [
352 {
353 "type": "simple",
354 "value": 42
355 },
356 {
357 "people": [
358 {"name": "Alice", "age": 30},
359 {"name": "Bob", "age": 25}
360 ],
361 "type": "complex"
362 },
363 {
364 "tags": ["a", "b", "c"],
365 "type": "array"
366 }
367 ]
368 });
369
370 let encoded = encode_default(&original).unwrap();
371 let decoded: Value = decode_default(&encoded).unwrap();
372
373 assert_eq!(original, decoded);
374 }
375}