1pub mod context;
47pub mod error;
48pub mod keywords;
49pub mod parser;
50pub mod serializer;
51
52pub use context::JsonLdContext;
54pub use error::{Result, ToonError};
55pub use keywords::*;
56pub use parser::ToonParser;
57pub use serializer::ToonSerializer;
58
59use serde_json::Value;
60
61pub fn encode(json: &str) -> Result<String> {
63 let value: serde_json::Value = serde_json::from_str(json).map_err(ToonError::from)?;
64 let context = JsonLdContext::from_value(&value);
65 let serializer = ToonSerializer::new().with_context(context);
66 serializer.serialize(&value)
67}
68
69pub fn decode(toon: &str) -> Result<String> {
71 let parser = ToonParser::new();
72 let value = parser.parse(toon)?;
73 serde_json::to_string_pretty(&value).map_err(ToonError::from)
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_primitive_values() {
82 let serializer = ToonSerializer::new();
83 let parser = ToonParser::new();
84
85 let json = r#"{"name": "Alice", "age": 30, "active": true}"#;
86 let toon = serializer.serialize_json(json).unwrap();
87 let back = parser.parse(&toon).unwrap();
88
89 assert_eq!(back.get("name").unwrap(), "Alice");
90 assert_eq!(back.get("age").unwrap(), 30);
91 assert_eq!(back.get("active").unwrap(), true);
92 }
93
94 #[test]
95 fn test_tabular_array() {
96 let serializer = ToonSerializer::new();
97
98 let json = r#"{
99 "people": [
100 {"name": "Alice", "age": 30},
101 {"name": "Bob", "age": 25}
102 ]
103 }"#;
104
105 let toon = serializer.serialize_json(json).unwrap();
106 assert!(toon.contains("people[2]{"));
107 assert!(toon.contains("Alice"));
108 assert!(toon.contains("Bob"));
109 }
110
111 #[test]
112 fn test_primitive_array() {
113 let serializer = ToonSerializer::new();
114
115 let json = r#"{"tags": ["rust", "wasm", "python"]}"#;
116 let toon = serializer.serialize_json(json).unwrap();
117 assert!(toon.contains("tags[3]:"));
118 }
119
120 #[test]
121 fn test_quoting() {
122 let serializer = ToonSerializer::new();
123
124 let json = r#"{"note": "Hello, World"}"#;
126 let toon = serializer.serialize_json(json).unwrap();
127 assert!(toon.contains("\"Hello, World\""));
128 }
129
130 #[test]
131 fn test_roundtrip() {
132 let original = r#"{
133 "users": [
134 {"id": 1, "name": "Alice"},
135 {"id": 2, "name": "Bob"}
136 ],
137 "count": 2,
138 "active": true
139 }"#;
140
141 let toon = encode(original).unwrap();
142 let back_json = decode(&toon).unwrap();
143 let back: Value = serde_json::from_str(&back_json).unwrap();
144
145 assert_eq!(back.get("count").unwrap(), 2);
146 assert_eq!(back.get("active").unwrap(), true);
147 }
148
149 #[test]
150 fn test_jsonld_context() {
151 let json = r#"{
152 "@context": {
153 "foaf": "http://xmlns.com/foaf/0.1/"
154 },
155 "http://xmlns.com/foaf/0.1/name": "Alice"
156 }"#;
157
158 let toon = encode(json).unwrap();
159 assert!(toon.contains("foaf:name"));
160 }
161
162 #[test]
163 fn test_empty_array() {
164 let serializer = ToonSerializer::new();
165 let parser = ToonParser::new();
166
167 let json = r#"{"items": []}"#;
168 let toon = serializer.serialize_json(json).unwrap();
169 assert!(toon.contains("items[0]:"));
170
171 let back = parser.parse(&toon).unwrap();
172 assert!(back.get("items").unwrap().as_array().unwrap().is_empty());
173 }
174
175 #[test]
176 fn test_nested_objects() {
177 let serializer = ToonSerializer::new();
178
179 let json = r#"{
180 "person": {
181 "name": "Alice",
182 "address": {
183 "city": "Seattle",
184 "zip": "98101"
185 }
186 }
187 }"#;
188
189 let toon = serializer.serialize_json(json).unwrap();
190 assert!(toon.contains("person:"));
191 assert!(toon.contains("address:"));
192 assert!(toon.contains("city: Seattle"));
193 }
194
195 #[test]
196 fn test_missing_fields_in_tabular() {
197 let serializer = ToonSerializer::new().with_shape_partitioning(false);
199 let parser = ToonParser::new();
200
201 let json = r#"{
203 "items": [
204 {"a": 1, "b": 2},
205 {"a": 3, "c": 4}
206 ]
207 }"#;
208
209 let toon = serializer.serialize_json(json).unwrap();
210 assert!(toon.contains("items[2]{a,b,c}:"));
212 assert!(toon.contains("1, 2, null"));
214 assert!(toon.contains("3, null, 4"));
215
216 let back = parser.parse(&toon).unwrap();
218 let items = back.get("items").unwrap().as_array().unwrap();
219 assert_eq!(items[0].get("a").unwrap(), 1);
220 assert_eq!(items[0].get("b").unwrap(), 2);
221 assert!(items[0].get("c").unwrap().is_null());
222 assert_eq!(items[1].get("a").unwrap(), 3);
223 assert!(items[1].get("b").unwrap().is_null());
224 assert_eq!(items[1].get("c").unwrap(), 4);
225 }
226
227 #[test]
228 fn test_special_characters_in_values() {
229 let serializer = ToonSerializer::new();
230 let parser = ToonParser::new();
231
232 let json = r#"{"message": "Hello: World", "note": "A|B"}"#;
233 let toon = serializer.serialize_json(json).unwrap();
234
235 assert!(toon.contains("\"Hello: World\""));
237 assert!(toon.contains("\"A|B\""));
238
239 let back = parser.parse(&toon).unwrap();
240 assert_eq!(back.get("message").unwrap(), "Hello: World");
241 assert_eq!(back.get("note").unwrap(), "A|B");
242 }
243
244 #[test]
245 fn test_jsonld_id_and_type() {
246 let serializer = ToonSerializer::new();
247 let parser = ToonParser::new();
248
249 let json = r#"{
250 "@id": "http://example.org/person/1",
251 "@type": "Person",
252 "name": "Alice"
253 }"#;
254
255 let toon = serializer.serialize_json(json).unwrap();
256
257 assert!(toon.contains("@id:"));
258 assert!(toon.contains("@type: Person"));
259 assert!(toon.contains("name: Alice"));
260
261 let back = parser.parse(&toon).unwrap();
263 assert_eq!(back.get("@id").unwrap(), "http://example.org/person/1");
264 assert_eq!(back.get("@type").unwrap(), "Person");
265 assert_eq!(back.get("name").unwrap(), "Alice");
266 }
267
268 #[test]
269 fn test_jsonld_graph() {
270 let serializer = ToonSerializer::new();
271 let parser = ToonParser::new();
272
273 let json = r#"{
274 "@context": {
275 "foaf": "http://xmlns.com/foaf/0.1/"
276 },
277 "@graph": [
278 {"@id": "http://example.org/1", "@type": "Person", "foaf:name": "Alice"},
279 {"@id": "http://example.org/2", "@type": "Person", "foaf:name": "Bob"}
280 ]
281 }"#;
282
283 let toon = serializer.serialize_json(json).unwrap();
284
285 assert!(toon.contains("@graph[2]"));
287 assert!(toon.contains("@id"));
288 assert!(toon.contains("@type"));
289
290 let back = parser.parse(&toon).unwrap();
292 let graph = back.get("@graph").unwrap().as_array().unwrap();
293 assert_eq!(graph.len(), 2);
294 assert_eq!(graph[0].get("@type").unwrap(), "Person");
295 }
296
297 #[test]
298 fn test_jsonld_type_array() {
299 let serializer = ToonSerializer::new();
300 let parser = ToonParser::new();
301
302 let json = r#"{
304 "@id": "http://example.org/1",
305 "@type": ["Person", "Agent"],
306 "name": "Alice"
307 }"#;
308
309 let toon = serializer.serialize_json(json).unwrap();
310 assert!(toon.contains("@type"));
311 assert!(toon.contains("Person"));
312 assert!(toon.contains("Agent"));
313
314 let back = parser.parse(&toon).unwrap();
315 let types = back.get("@type").unwrap().as_array().unwrap();
316 assert_eq!(types.len(), 2);
317 }
318
319 #[test]
320 fn test_jsonld_full_document() {
321 let json = r#"{
322 "@context": {
323 "foaf": "http://xmlns.com/foaf/0.1/",
324 "schema": "http://schema.org/"
325 },
326 "@id": "http://example.org/dataset",
327 "@type": "schema:Dataset",
328 "@graph": [
329 {
330 "@id": "http://example.org/person/1",
331 "@type": "foaf:Person",
332 "foaf:name": "Alice",
333 "foaf:age": 30
334 },
335 {
336 "@id": "http://example.org/person/2",
337 "@type": "foaf:Person",
338 "foaf:name": "Bob",
339 "foaf:age": 25
340 }
341 ]
342 }"#;
343
344 let toon = encode(json).unwrap();
345
346 assert!(toon.starts_with("@context:"));
348 assert!(toon.contains("@id:"));
349 assert!(toon.contains("http://example.org/dataset"));
350 assert!(toon.contains("@type:"));
351 assert!(toon.contains("@graph[2]"));
352
353 let back_json = decode(&toon).unwrap();
355 let back: Value = serde_json::from_str(&back_json).unwrap();
356
357 assert_eq!(back.get("@id").unwrap(), "http://example.org/dataset");
358 let graph = back.get("@graph").unwrap().as_array().unwrap();
359 assert_eq!(graph.len(), 2);
360 }
361
362 #[test]
363 fn test_jsonld_value_with_language() {
364 let json = r#"{
365 "title": {"@value": "Bonjour", "@language": "fr"}
366 }"#;
367
368 let toon = encode(json).unwrap();
369
370 assert!(toon.contains("@value"));
372 assert!(toon.contains("Bonjour"));
373 assert!(toon.contains("@language"));
374 assert!(toon.contains("fr"));
375 }
376
377 #[test]
378 fn test_jsonld_value_with_type() {
379 let json = r#"{
380 "@context": {
381 "xsd": "http://www.w3.org/2001/XMLSchema#"
382 },
383 "date": {"@value": "2024-01-15", "@type": "http://www.w3.org/2001/XMLSchema#date"}
384 }"#;
385
386 let toon = encode(json).unwrap();
387
388 assert!(toon.contains("@value"));
390 assert!(toon.contains("2024-01-15"));
391 assert!(toon.contains("@type"));
392 assert!(toon.contains("xsd:date"));
393 }
394
395 #[test]
396 fn test_jsonld_list() {
397 let serializer = ToonSerializer::new();
398 let parser = ToonParser::new();
399
400 let json = r#"{
401 "sequence": {"@list": ["first", "second", "third"]}
402 }"#;
403
404 let toon = serializer.serialize_json(json).unwrap();
405
406 assert!(toon.contains("@list"));
407 assert!(toon.contains("first"));
408
409 let back = parser.parse(&toon).unwrap();
410 let seq = back.get("sequence").unwrap().as_object().unwrap();
411 let list = seq.get("@list").unwrap().as_array().unwrap();
412 assert_eq!(list.len(), 3);
413 }
414
415 #[test]
416 fn test_jsonld_set() {
417 let serializer = ToonSerializer::new();
418 let parser = ToonParser::new();
419
420 let json = r#"{
421 "tags": {"@set": ["rust", "wasm", "python"]}
422 }"#;
423
424 let toon = serializer.serialize_json(json).unwrap();
425
426 assert!(toon.contains("@set"));
427
428 let back = parser.parse(&toon).unwrap();
429 let tags = back.get("tags").unwrap().as_object().unwrap();
430 let set = tags.get("@set").unwrap().as_array().unwrap();
431 assert_eq!(set.len(), 3);
432 }
433
434 #[test]
435 fn test_jsonld_reverse() {
436 let serializer = ToonSerializer::new();
437 let parser = ToonParser::new();
438
439 let json = r#"{
440 "@id": "http://example.org/alice",
441 "@reverse": {
442 "foaf:knows": {"@id": "http://example.org/bob"}
443 }
444 }"#;
445
446 let toon = serializer.serialize_json(json).unwrap();
447
448 assert!(toon.contains("@reverse"));
449 assert!(toon.contains("foaf:knows"));
450
451 let back = parser.parse(&toon).unwrap();
452 assert!(back.get("@reverse").is_some());
453 }
454
455 #[test]
456 fn test_jsonld_base_and_vocab() {
457 let serializer = ToonSerializer::new();
458
459 let json = r#"{
460 "@context": {
461 "@base": "http://example.org/",
462 "@vocab": "http://schema.org/"
463 },
464 "@id": "person/1",
465 "name": "Alice"
466 }"#;
467
468 let toon = serializer.serialize_json(json).unwrap();
469
470 assert!(toon.contains("@base"));
471 assert!(toon.contains("@vocab"));
472 assert!(toon.contains("http://example.org/"));
473 assert!(toon.contains("http://schema.org/"));
474 }
475
476 #[test]
479 fn test_jsonld_version() {
480 let serializer = ToonSerializer::new();
481 let parser = ToonParser::new();
482
483 let json = r#"{
484 "@context": {
485 "@version": 1.1,
486 "name": "http://schema.org/name"
487 },
488 "name": "Alice"
489 }"#;
490
491 let toon = serializer.serialize_json(json).unwrap();
492
493 assert!(toon.contains("@version"));
494 assert!(toon.contains("1.1"));
495
496 let back = parser.parse(&toon).unwrap();
497 let ctx = back.get("@context").unwrap().as_object().unwrap();
498 assert!(ctx.get("@version").is_some());
499 }
500
501 #[test]
502 fn test_jsonld_direction() {
503 let serializer = ToonSerializer::new();
504 let parser = ToonParser::new();
505
506 let json = r#"{
507 "@context": {
508 "@direction": "rtl"
509 },
510 "text": "Hello"
511 }"#;
512
513 let toon = serializer.serialize_json(json).unwrap();
514
515 assert!(toon.contains("@direction"));
516 assert!(toon.contains("rtl"));
517
518 let back = parser.parse(&toon).unwrap();
519 let ctx = back.get("@context").unwrap().as_object().unwrap();
520 assert_eq!(ctx.get("@direction").unwrap(), "rtl");
521 }
522
523 #[test]
524 fn test_jsonld_value_with_direction() {
525 let json = r#"{
526 "title": {"@value": "مرحبا", "@language": "ar", "@direction": "rtl"}
527 }"#;
528
529 let toon = encode(json).unwrap();
530
531 assert!(toon.contains("@value"));
533 assert!(toon.contains("مرحبا"));
534 assert!(toon.contains("@language"));
535 assert!(toon.contains("ar"));
536 assert!(toon.contains("@direction"));
537 assert!(toon.contains("rtl"));
538 }
539
540 #[test]
541 fn test_jsonld_container() {
542 let serializer = ToonSerializer::new();
543 let parser = ToonParser::new();
544
545 let json = r#"{
546 "@context": {
547 "tags": {
548 "@id": "http://example.org/tags",
549 "@container": "@set"
550 }
551 },
552 "tags": ["a", "b", "c"]
553 }"#;
554
555 let toon = serializer.serialize_json(json).unwrap();
556
557 assert!(toon.contains("@container"));
558
559 let back = parser.parse(&toon).unwrap();
560 assert!(back.get("@context").is_some());
561 }
562
563 #[test]
564 fn test_jsonld_container_array() {
565 let serializer = ToonSerializer::new();
566 let parser = ToonParser::new();
567
568 let json = r#"{
569 "@container": ["@index", "@set"]
570 }"#;
571
572 let toon = serializer.serialize_json(json).unwrap();
573
574 assert!(toon.contains("@container"));
575
576 let back = parser.parse(&toon).unwrap();
577 let container = back.get("@container").unwrap().as_array().unwrap();
578 assert_eq!(container.len(), 2);
579 }
580
581 #[test]
582 fn test_jsonld_index() {
583 let serializer = ToonSerializer::new();
584 let parser = ToonParser::new();
585
586 let json = r#"{
587 "@id": "http://example.org/1",
588 "@index": "chapter1",
589 "title": "Introduction"
590 }"#;
591
592 let toon = serializer.serialize_json(json).unwrap();
593
594 assert!(toon.contains("@index"));
595 assert!(toon.contains("chapter1"));
596
597 let back = parser.parse(&toon).unwrap();
598 assert_eq!(back.get("@index").unwrap(), "chapter1");
599 }
600
601 #[test]
602 fn test_jsonld_included() {
603 let serializer = ToonSerializer::new();
604 let parser = ToonParser::new();
605
606 let json = r#"{
607 "@id": "http://example.org/main",
608 "name": "Main Resource",
609 "@included": [
610 {"@id": "http://example.org/ref1", "name": "Reference 1"},
611 {"@id": "http://example.org/ref2", "name": "Reference 2"}
612 ]
613 }"#;
614
615 let toon = serializer.serialize_json(json).unwrap();
616
617 assert!(toon.contains("@included"));
618 assert!(toon.contains("Reference 1"));
619 assert!(toon.contains("Reference 2"));
620
621 let back = parser.parse(&toon).unwrap();
622 let included = back.get("@included").unwrap().as_array().unwrap();
623 assert_eq!(included.len(), 2);
624 }
625
626 #[test]
627 fn test_jsonld_nest() {
628 let serializer = ToonSerializer::new();
629 let parser = ToonParser::new();
630
631 let json = r#"{
632 "@id": "http://example.org/1",
633 "@nest": {
634 "nested_prop": "nested_value"
635 }
636 }"#;
637
638 let toon = serializer.serialize_json(json).unwrap();
639
640 assert!(toon.contains("@nest"));
641 assert!(toon.contains("nested_prop"));
642 assert!(toon.contains("nested_value"));
643
644 let back = parser.parse(&toon).unwrap();
645 let nest = back.get("@nest").unwrap().as_object().unwrap();
646 assert_eq!(nest.get("nested_prop").unwrap(), "nested_value");
647 }
648
649 #[test]
650 fn test_jsonld_prefix() {
651 let serializer = ToonSerializer::new();
652 let parser = ToonParser::new();
653
654 let json = r#"{
655 "@context": {
656 "ex": {
657 "@id": "http://example.org/",
658 "@prefix": true
659 }
660 },
661 "ex:name": "Test"
662 }"#;
663
664 let toon = serializer.serialize_json(json).unwrap();
665
666 assert!(toon.contains("@prefix"));
667 assert!(toon.contains("true"));
668
669 let back = parser.parse(&toon).unwrap();
670 assert!(back.get("@context").is_some());
671 }
672
673 #[test]
674 fn test_jsonld_propagate() {
675 let serializer = ToonSerializer::new();
676 let parser = ToonParser::new();
677
678 let json = r#"{
679 "@context": {
680 "@propagate": false,
681 "name": "http://schema.org/name"
682 },
683 "name": "Alice"
684 }"#;
685
686 let toon = serializer.serialize_json(json).unwrap();
687
688 assert!(toon.contains("@propagate"));
689 assert!(toon.contains("false"));
690
691 let back = parser.parse(&toon).unwrap();
692 let ctx = back.get("@context").unwrap().as_object().unwrap();
693 assert_eq!(ctx.get("@propagate").unwrap(), false);
694 }
695
696 #[test]
697 fn test_jsonld_protected() {
698 let serializer = ToonSerializer::new();
699 let parser = ToonParser::new();
700
701 let json = r#"{
702 "@context": {
703 "name": {
704 "@id": "http://schema.org/name",
705 "@protected": true
706 }
707 },
708 "name": "Alice"
709 }"#;
710
711 let toon = serializer.serialize_json(json).unwrap();
712
713 assert!(toon.contains("@protected"));
714 assert!(toon.contains("true"));
715
716 let _back = parser.parse(&toon).unwrap();
717 }
718
719 #[test]
720 fn test_jsonld_import() {
721 let serializer = ToonSerializer::new();
722 let parser = ToonParser::new();
723
724 let json = r#"{
725 "@context": {
726 "@import": "http://example.org/context.jsonld"
727 },
728 "name": "Alice"
729 }"#;
730
731 let toon = serializer.serialize_json(json).unwrap();
732
733 assert!(toon.contains("@import"));
734 assert!(toon.contains("http://example.org/context.jsonld"));
735
736 let back = parser.parse(&toon).unwrap();
737 let ctx = back.get("@context").unwrap().as_object().unwrap();
738 assert!(ctx.get("@import").is_some());
739 }
740
741 #[test]
742 fn test_jsonld_none() {
743 let serializer = ToonSerializer::new();
744 let parser = ToonParser::new();
745
746 let json = r#"{
747 "@none": {"name": "Default item"}
748 }"#;
749
750 let toon = serializer.serialize_json(json).unwrap();
751
752 assert!(toon.contains("@none"));
753
754 let back = parser.parse(&toon).unwrap();
755 assert!(back.get("@none").is_some());
756 }
757
758 #[test]
759 fn test_jsonld_json_literal() {
760 let serializer = ToonSerializer::new();
761 let parser = ToonParser::new();
762
763 let json = r#"{
764 "data": {
765 "@value": {"nested": "object", "count": 42},
766 "@type": "@json"
767 }
768 }"#;
769
770 let toon = serializer.serialize_json(json).unwrap();
771
772 assert!(toon.contains("data"));
773
774 let back = parser.parse(&toon).unwrap();
775 assert!(back.get("data").is_some());
776 }
777
778 #[test]
779 fn test_jsonld_context_with_term_definitions() {
780 let json = r#"{
781 "@context": {
782 "@version": 1.1,
783 "name": {
784 "@id": "http://schema.org/name",
785 "@container": "@set",
786 "@protected": true
787 },
788 "knows": {
789 "@id": "http://xmlns.com/foaf/0.1/knows",
790 "@type": "@id"
791 }
792 },
793 "name": ["Alice", "Alicia"],
794 "knows": "http://example.org/bob"
795 }"#;
796
797 let toon = encode(json).unwrap();
798
799 assert!(toon.contains("@context"));
800 assert!(toon.contains("@version"));
801 assert!(toon.contains("name"));
802 assert!(toon.contains("knows"));
803
804 let back_json = decode(&toon).unwrap();
805 let back: Value = serde_json::from_str(&back_json).unwrap();
806 assert!(back.get("@context").is_some());
807 }
808
809 #[test]
810 fn test_jsonld_full_1_1_document() {
811 let json = r#"{
812 "@context": {
813 "@version": 1.1,
814 "@base": "http://example.org/",
815 "@vocab": "http://schema.org/",
816 "foaf": "http://xmlns.com/foaf/0.1/",
817 "@direction": "ltr",
818 "name": {
819 "@id": "foaf:name",
820 "@protected": true
821 }
822 },
823 "@id": "person/1",
824 "@type": "Person",
825 "@index": "main",
826 "name": "Alice",
827 "@included": [
828 {"@id": "person/2", "name": "Bob"}
829 ]
830 }"#;
831
832 let toon = encode(json).unwrap();
833
834 assert!(toon.contains("@context"));
835 assert!(toon.contains("@version"));
836 assert!(toon.contains("@base"));
837 assert!(toon.contains("@vocab"));
838 assert!(toon.contains("@direction"));
839 assert!(toon.contains("@id"));
840 assert!(toon.contains("@type"));
841 assert!(toon.contains("@index"));
842 assert!(toon.contains("@included"));
843 assert!(toon.contains("Alice"));
844 assert!(toon.contains("Bob"));
845
846 let back_json = decode(&toon).unwrap();
847 let back: Value = serde_json::from_str(&back_json).unwrap();
848 assert!(back.get("@context").is_some());
849 assert!(back.get("@included").is_some());
850 }
851}