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