1use crate::error::Error;
4use crate::options::EncodeOptions;
5use serde_json::Value;
6use std::io::Write;
7
8pub fn encode(value: &Value, options: Option<&EncodeOptions>) -> Result<String, Error> {
19 let default_opts = EncodeOptions::default();
20 let opts = options.unwrap_or(&default_opts);
21 let mut output = String::new();
22 encode_value(value, &mut output, 0, opts)?;
23 Ok(output)
24}
25
26fn encode_value(
27 value: &Value,
28 output: &mut String,
29 indent_level: usize,
30 options: &EncodeOptions,
31) -> Result<(), Error> {
32 match value {
33 Value::Null => {
34 }
36 Value::Bool(b) => {
37 output.push_str(if *b { "true" } else { "false" });
38 }
39 Value::Number(n) => {
40 if let Some(i) = n.as_i64() {
41 output.push_str(&i.to_string());
42 } else if let Some(f) = n.as_f64() {
43 output.push_str(&f.to_string());
44 } else {
45 return Err(Error::Serialization("Invalid number".to_string()));
46 }
47 }
48 Value::String(s) => {
49 encode_string(s, output, options.get_delimiter());
50 }
51 Value::Array(arr) => {
52 encode_array(arr, output, indent_level, options)?;
53 }
54 Value::Object(obj) => {
55 encode_object(obj, output, indent_level, options)?;
56 }
57 }
58 Ok(())
59}
60
61fn encode_string(s: &str, output: &mut String, delimiter: char) {
62 let needs_quoting = s.contains(delimiter)
64 || s.contains(' ')
65 || s.contains('\n')
66 || s.contains('\t')
67 || s == "true"
68 || s == "false"
69 || s == "null"
70 || s.parse::<f64>().is_ok();
71
72 if needs_quoting {
73 output.push('"');
74 for ch in s.chars() {
75 match ch {
76 '"' => output.push_str("\\\""),
77 '\\' => output.push_str("\\\\"),
78 '\n' => output.push_str("\\n"),
79 '\r' => output.push_str("\\r"),
80 '\t' => output.push_str("\\t"),
81 _ => output.push(ch),
82 }
83 }
84 output.push('"');
85 } else {
86 output.push_str(s);
87 }
88}
89
90fn encode_array(
91 arr: &[Value],
92 output: &mut String,
93 indent_level: usize,
94 options: &EncodeOptions,
95) -> Result<(), Error> {
96 if arr.is_empty() {
97 output.push_str("[0]:");
98 return Ok(());
99 }
100
101 if let Some(keys) = check_uniform_objects(arr) {
103 let length_marker = options
105 .length_marker
106 .map(|m| format!("{m}"))
107 .unwrap_or_default();
108 output.push_str(&format!("[{}{}]", length_marker, arr.len()));
109 output.push('{');
110 output.push_str(&keys.join(&options.get_delimiter().to_string()));
111 output.push_str("}:\n");
112 encode_tabular_array_rows(arr, keys, output, indent_level, options)?;
113 return Ok(());
114 }
115
116 if arr.iter().all(is_primitive) {
118 encode_inline_array(arr, output, options)?;
119 return Ok(());
120 }
121
122 encode_list_array(arr, output, indent_level, options)?;
124 Ok(())
125}
126
127fn is_primitive(value: &Value) -> bool {
128 matches!(
129 value,
130 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_)
131 )
132}
133
134fn check_uniform_objects(arr: &[Value]) -> Option<Vec<String>> {
135 if arr.is_empty() {
136 return None;
137 }
138
139 let first = arr[0].as_object()?;
141 let keys: Vec<String> = first.keys().cloned().collect();
142 if keys.is_empty() {
143 return None;
144 }
145
146 for item in arr.iter().skip(1) {
148 let obj = item.as_object()?;
149 let item_keys: std::collections::HashSet<String> = obj.keys().cloned().collect();
150 let first_keys: std::collections::HashSet<String> = keys.iter().cloned().collect();
151 if item_keys != first_keys {
152 return None;
153 }
154 }
155
156 Some(keys)
157}
158
159fn encode_tabular_array_rows(
160 arr: &[Value],
161 keys: Vec<String>,
162 output: &mut String,
163 indent_level: usize,
164 options: &EncodeOptions,
165) -> Result<(), Error> {
166 let indent = options.get_indent();
167 let indent_str = " ".repeat(indent_level * indent);
168 let delimiter = options.get_delimiter();
169
170 for item in arr {
172 output.push_str(&indent_str);
173 output.push_str(&" ".repeat(indent));
174 let obj = item
175 .as_object()
176 .ok_or_else(|| Error::Serialization("Expected object in tabular array".to_string()))?;
177
178 let mut first = true;
179 for key in &keys {
180 if !first {
181 output.push(delimiter);
182 }
183 let value = obj
184 .get(key)
185 .ok_or_else(|| Error::Serialization(format!("Missing key: {key}")))?;
186 encode_primitive_value(value, output, delimiter)?;
187 first = false;
188 }
189 output.push('\n');
190 }
191
192 Ok(())
193}
194
195fn encode_primitive_value(
196 value: &Value,
197 output: &mut String,
198 delimiter: char,
199) -> Result<(), Error> {
200 match value {
201 Value::Null => {}
202 Value::Bool(b) => {
203 output.push_str(if *b { "true" } else { "false" });
204 }
205 Value::Number(n) => {
206 if let Some(i) = n.as_i64() {
207 output.push_str(&i.to_string());
208 } else if let Some(f) = n.as_f64() {
209 output.push_str(&f.to_string());
210 } else {
211 return Err(Error::Serialization("Invalid number".to_string()));
212 }
213 }
214 Value::String(s) => {
215 encode_string(s, output, delimiter);
216 }
217 _ => {
218 return Err(Error::Serialization(
219 "Non-primitive value in tabular array".to_string(),
220 ));
221 }
222 }
223 Ok(())
224}
225
226fn encode_inline_array(
227 arr: &[Value],
228 output: &mut String,
229 options: &EncodeOptions,
230) -> Result<(), Error> {
231 let length_marker = options
232 .length_marker
233 .map(|m| format!("{m}"))
234 .unwrap_or_default();
235 output.push_str(&format!("[{}{}]:", length_marker, arr.len()));
236
237 let delimiter = options.get_delimiter();
238 let mut first = true;
239 for item in arr {
240 if !first {
241 output.push(delimiter);
242 }
243 match item {
244 Value::Null => {}
245 Value::Bool(b) => {
246 output.push_str(if *b { "true" } else { "false" });
247 }
248 Value::Number(n) => {
249 if let Some(i) = n.as_i64() {
250 output.push_str(&i.to_string());
251 } else if let Some(f) = n.as_f64() {
252 output.push_str(&f.to_string());
253 }
254 }
255 Value::String(s) => {
256 encode_string(s, output, delimiter);
257 }
258 _ => {
259 return Err(Error::Serialization(
260 "Non-primitive in inline array".to_string(),
261 ));
262 }
263 }
264 first = false;
265 }
266
267 Ok(())
268}
269
270fn encode_list_array(
271 arr: &[Value],
272 output: &mut String,
273 indent_level: usize,
274 options: &EncodeOptions,
275) -> Result<(), Error> {
276 let indent = options.get_indent();
277 let indent_str = " ".repeat(indent_level * indent);
278
279 for item in arr {
280 output.push_str(&indent_str);
281 output.push_str(&" ".repeat(indent));
282 output.push_str("- ");
283 match item {
285 Value::Object(obj) => {
286 let mut first = true;
287 for (key, val) in obj {
288 if !first {
289 output.push(' ');
290 }
291 output.push_str(key);
292 output.push_str(": ");
293 encode_primitive_value(val, output, options.get_delimiter())?;
294 first = false;
295 }
296 }
297 _ => {
298 encode_value(item, output, indent_level + 1, options)?;
299 }
300 }
301 output.push('\n');
302 }
303
304 Ok(())
305}
306
307fn encode_object(
308 obj: &serde_json::Map<String, Value>,
309 output: &mut String,
310 indent_level: usize,
311 options: &EncodeOptions,
312) -> Result<(), Error> {
313 if obj.is_empty() {
314 return Ok(());
315 }
316
317 let indent = options.get_indent();
318 let indent_str = " ".repeat(indent_level * indent);
319
320 let mut first = true;
321 for (key, value) in obj {
322 if !first {
323 output.push('\n');
324 }
325 output.push_str(&indent_str);
326 output.push_str(key);
327
328 match value {
329 Value::Array(arr) => {
330 if arr.is_empty() {
332 output.push_str("[0]:");
333 } else if let Some(keys) = check_uniform_objects(arr) {
334 let length_marker = options
336 .length_marker
337 .map(|m| format!("{m}"))
338 .unwrap_or_default();
339 output.push_str(&format!("[{}{}]", length_marker, arr.len()));
340 output.push('{');
341 output.push_str(&keys.join(&options.get_delimiter().to_string()));
342 output.push_str("}:\n");
343 encode_tabular_array_rows(arr, keys, output, indent_level, options)?;
345 } else if arr.iter().all(is_primitive) {
346 let length_marker = options
348 .length_marker
349 .map(|m| format!("{m}"))
350 .unwrap_or_default();
351 output.push_str(&format!("[{}{}]:", length_marker, arr.len()));
352 let delimiter = options.get_delimiter();
353 let mut first = true;
354 for item in arr {
355 if !first {
356 output.push(delimiter);
357 }
358 encode_primitive_value(item, output, delimiter)?;
359 first = false;
360 }
361 } else {
362 let length_marker = options
364 .length_marker
365 .map(|m| format!("{m}"))
366 .unwrap_or_default();
367 output.push_str(&format!("[{}{}]:", length_marker, arr.len()));
368 output.push('\n');
369 encode_list_array(arr, output, indent_level, options)?;
370 }
371 }
372 Value::Object(_) => {
373 output.push_str(": ");
374 output.push('\n');
375 encode_value(value, output, indent_level + 1, options)?;
376 }
377 _ => {
378 output.push_str(": ");
379 encode_value(value, output, indent_level, options)?;
380 }
381 }
382 first = false;
383 }
384
385 Ok(())
386}
387
388pub fn encode_stream<W: Write>(
417 value: &Value,
418 writer: &mut W,
419 options: Option<&EncodeOptions>,
420) -> Result<(), Error> {
421 let default_opts = EncodeOptions::default();
422 let opts = options.unwrap_or(&default_opts);
423 encode_value_to_writer(value, writer, 0, opts)?;
424 writer.flush().map_err(|e| Error::Io(e.to_string()))?;
425 Ok(())
426}
427
428fn encode_value_to_writer<W: Write>(
429 value: &Value,
430 writer: &mut W,
431 indent_level: usize,
432 options: &EncodeOptions,
433) -> Result<(), Error> {
434 match value {
435 Value::Null => {
436 }
438 Value::Bool(b) => {
439 let s = if *b { "true" } else { "false" };
440 writer
441 .write_all(s.as_bytes())
442 .map_err(|e| Error::Io(e.to_string()))?;
443 }
444 Value::Number(n) => {
445 if let Some(i) = n.as_i64() {
446 let s = i.to_string();
447 writer
448 .write_all(s.as_bytes())
449 .map_err(|e| Error::Io(e.to_string()))?;
450 } else if let Some(f) = n.as_f64() {
451 let s = f.to_string();
452 writer
453 .write_all(s.as_bytes())
454 .map_err(|e| Error::Io(e.to_string()))?;
455 } else {
456 return Err(Error::Serialization("Invalid number".to_string()));
457 }
458 }
459 Value::String(s) => {
460 encode_string_to_writer(s, writer, options.get_delimiter())?;
461 }
462 Value::Array(arr) => {
463 encode_array_to_writer(arr, writer, indent_level, options)?;
464 }
465 Value::Object(obj) => {
466 encode_object_to_writer(obj, writer, indent_level, options)?;
467 }
468 }
469 Ok(())
470}
471
472fn encode_string_to_writer<W: Write>(
473 s: &str,
474 writer: &mut W,
475 delimiter: char,
476) -> Result<(), Error> {
477 let needs_quoting = s.contains(delimiter)
479 || s.contains(' ')
480 || s.contains('\n')
481 || s.contains('\t')
482 || s == "true"
483 || s == "false"
484 || s == "null"
485 || s.parse::<f64>().is_ok();
486
487 if needs_quoting {
488 writer
489 .write_all(b"\"")
490 .map_err(|e| Error::Io(e.to_string()))?;
491 for ch in s.chars() {
492 match ch {
493 '"' => writer
494 .write_all(b"\\\"")
495 .map_err(|e| Error::Io(e.to_string()))?,
496 '\\' => writer
497 .write_all(b"\\\\")
498 .map_err(|e| Error::Io(e.to_string()))?,
499 '\n' => writer
500 .write_all(b"\\n")
501 .map_err(|e| Error::Io(e.to_string()))?,
502 '\r' => writer
503 .write_all(b"\\r")
504 .map_err(|e| Error::Io(e.to_string()))?,
505 '\t' => writer
506 .write_all(b"\\t")
507 .map_err(|e| Error::Io(e.to_string()))?,
508 _ => {
509 let mut buf = [0; 4];
510 let bytes = ch.encode_utf8(&mut buf).as_bytes();
511 writer
512 .write_all(bytes)
513 .map_err(|e| Error::Io(e.to_string()))?;
514 }
515 }
516 }
517 writer
518 .write_all(b"\"")
519 .map_err(|e| Error::Io(e.to_string()))?;
520 } else {
521 writer
522 .write_all(s.as_bytes())
523 .map_err(|e| Error::Io(e.to_string()))?;
524 }
525 Ok(())
526}
527
528fn encode_array_to_writer<W: Write>(
529 arr: &[Value],
530 writer: &mut W,
531 indent_level: usize,
532 options: &EncodeOptions,
533) -> Result<(), Error> {
534 if arr.is_empty() {
535 writer
536 .write_all(b"[0]:")
537 .map_err(|e| Error::Io(e.to_string()))?;
538 return Ok(());
539 }
540
541 if let Some(keys) = check_uniform_objects(arr) {
543 let length_marker = options
545 .length_marker
546 .map(|m| format!("{m}"))
547 .unwrap_or_default();
548 let header = format!("[{}{}]", length_marker, arr.len());
549 writer
550 .write_all(header.as_bytes())
551 .map_err(|e| Error::Io(e.to_string()))?;
552 writer
553 .write_all(b"{")
554 .map_err(|e| Error::Io(e.to_string()))?;
555 let keys_str = keys.join(&options.get_delimiter().to_string());
556 writer
557 .write_all(keys_str.as_bytes())
558 .map_err(|e| Error::Io(e.to_string()))?;
559 writer
560 .write_all(b"}:\n")
561 .map_err(|e| Error::Io(e.to_string()))?;
562 encode_tabular_array_rows_to_writer(arr, keys, writer, indent_level, options)?;
563 return Ok(());
564 }
565
566 if arr.iter().all(is_primitive) {
568 encode_inline_array_to_writer(arr, writer, options)?;
569 return Ok(());
570 }
571
572 encode_list_array_to_writer(arr, writer, indent_level, options)?;
574 Ok(())
575}
576
577fn encode_tabular_array_rows_to_writer<W: Write>(
578 arr: &[Value],
579 keys: Vec<String>,
580 writer: &mut W,
581 indent_level: usize,
582 options: &EncodeOptions,
583) -> Result<(), Error> {
584 let indent = options.get_indent();
585 let indent_str = " ".repeat(indent_level * indent);
586 let delimiter = options.get_delimiter();
587
588 for item in arr {
590 writer
591 .write_all(indent_str.as_bytes())
592 .map_err(|e| Error::Io(e.to_string()))?;
593 writer
594 .write_all(" ".repeat(indent).as_bytes())
595 .map_err(|e| Error::Io(e.to_string()))?;
596 let obj = item
597 .as_object()
598 .ok_or_else(|| Error::Serialization("Expected object in tabular array".to_string()))?;
599
600 let mut first = true;
601 for key in &keys {
602 if !first {
603 let delim_bytes = [delimiter as u8];
604 writer
605 .write_all(&delim_bytes)
606 .map_err(|e| Error::Io(e.to_string()))?;
607 }
608 let value = obj
609 .get(key)
610 .ok_or_else(|| Error::Serialization(format!("Missing key: {key}")))?;
611 encode_primitive_value_to_writer(value, writer, delimiter)?;
612 first = false;
613 }
614 writer
615 .write_all(b"\n")
616 .map_err(|e| Error::Io(e.to_string()))?;
617 }
618
619 Ok(())
620}
621
622fn encode_primitive_value_to_writer<W: Write>(
623 value: &Value,
624 writer: &mut W,
625 delimiter: char,
626) -> Result<(), Error> {
627 match value {
628 Value::Null => {}
629 Value::Bool(b) => {
630 let s = if *b { "true" } else { "false" };
631 writer
632 .write_all(s.as_bytes())
633 .map_err(|e| Error::Io(e.to_string()))?;
634 }
635 Value::Number(n) => {
636 if let Some(i) = n.as_i64() {
637 let s = i.to_string();
638 writer
639 .write_all(s.as_bytes())
640 .map_err(|e| Error::Io(e.to_string()))?;
641 } else if let Some(f) = n.as_f64() {
642 let s = f.to_string();
643 writer
644 .write_all(s.as_bytes())
645 .map_err(|e| Error::Io(e.to_string()))?;
646 } else {
647 return Err(Error::Serialization("Invalid number".to_string()));
648 }
649 }
650 Value::String(s) => {
651 encode_string_to_writer(s, writer, delimiter)?;
652 }
653 _ => {
654 return Err(Error::Serialization(
655 "Non-primitive value in tabular array".to_string(),
656 ));
657 }
658 }
659 Ok(())
660}
661
662fn encode_inline_array_to_writer<W: Write>(
663 arr: &[Value],
664 writer: &mut W,
665 options: &EncodeOptions,
666) -> Result<(), Error> {
667 let length_marker = options
668 .length_marker
669 .map(|m| format!("{m}"))
670 .unwrap_or_default();
671 let header = format!("[{}{}]:", length_marker, arr.len());
672 writer
673 .write_all(header.as_bytes())
674 .map_err(|e| Error::Io(e.to_string()))?;
675
676 let delimiter = options.get_delimiter();
677 let mut first = true;
678 for item in arr {
679 if !first {
680 let delim_bytes = [delimiter as u8];
681 writer
682 .write_all(&delim_bytes)
683 .map_err(|e| Error::Io(e.to_string()))?;
684 }
685 match item {
686 Value::Null => {}
687 Value::Bool(b) => {
688 let s = if *b { "true" } else { "false" };
689 writer
690 .write_all(s.as_bytes())
691 .map_err(|e| Error::Io(e.to_string()))?;
692 }
693 Value::Number(n) => {
694 if let Some(i) = n.as_i64() {
695 let s = i.to_string();
696 writer
697 .write_all(s.as_bytes())
698 .map_err(|e| Error::Io(e.to_string()))?;
699 } else if let Some(f) = n.as_f64() {
700 let s = f.to_string();
701 writer
702 .write_all(s.as_bytes())
703 .map_err(|e| Error::Io(e.to_string()))?;
704 }
705 }
706 Value::String(s) => {
707 encode_string_to_writer(s, writer, delimiter)?;
708 }
709 _ => {
710 return Err(Error::Serialization(
711 "Non-primitive in inline array".to_string(),
712 ));
713 }
714 }
715 first = false;
716 }
717
718 Ok(())
719}
720
721fn encode_list_array_to_writer<W: Write>(
722 arr: &[Value],
723 writer: &mut W,
724 indent_level: usize,
725 options: &EncodeOptions,
726) -> Result<(), Error> {
727 let indent = options.get_indent();
728 let indent_str = " ".repeat(indent_level * indent);
729
730 for item in arr {
731 writer
732 .write_all(indent_str.as_bytes())
733 .map_err(|e| Error::Io(e.to_string()))?;
734 writer
735 .write_all(" ".repeat(indent).as_bytes())
736 .map_err(|e| Error::Io(e.to_string()))?;
737 writer
738 .write_all(b"- ")
739 .map_err(|e| Error::Io(e.to_string()))?;
740 match item {
742 Value::Object(obj) => {
743 let mut first = true;
744 for (key, val) in obj {
745 if !first {
746 writer
747 .write_all(b" ")
748 .map_err(|e| Error::Io(e.to_string()))?;
749 }
750 writer
751 .write_all(key.as_bytes())
752 .map_err(|e| Error::Io(e.to_string()))?;
753 writer
754 .write_all(b": ")
755 .map_err(|e| Error::Io(e.to_string()))?;
756 encode_primitive_value_to_writer(val, writer, options.get_delimiter())?;
757 first = false;
758 }
759 }
760 _ => {
761 encode_value_to_writer(item, writer, indent_level + 1, options)?;
762 }
763 }
764 writer
765 .write_all(b"\n")
766 .map_err(|e| Error::Io(e.to_string()))?;
767 }
768
769 Ok(())
770}
771
772fn encode_object_to_writer<W: Write>(
773 obj: &serde_json::Map<String, Value>,
774 writer: &mut W,
775 indent_level: usize,
776 options: &EncodeOptions,
777) -> Result<(), Error> {
778 if obj.is_empty() {
779 return Ok(());
780 }
781
782 let indent = options.get_indent();
783 let indent_str = " ".repeat(indent_level * indent);
784
785 let mut first = true;
786 for (key, value) in obj {
787 if !first {
788 writer
789 .write_all(b"\n")
790 .map_err(|e| Error::Io(e.to_string()))?;
791 }
792 writer
793 .write_all(indent_str.as_bytes())
794 .map_err(|e| Error::Io(e.to_string()))?;
795 writer
796 .write_all(key.as_bytes())
797 .map_err(|e| Error::Io(e.to_string()))?;
798
799 match value {
800 Value::Array(arr) => {
801 if arr.is_empty() {
803 writer
804 .write_all(b"[0]:")
805 .map_err(|e| Error::Io(e.to_string()))?;
806 } else if let Some(keys) = check_uniform_objects(arr) {
807 let length_marker = options
809 .length_marker
810 .map(|m| format!("{m}"))
811 .unwrap_or_default();
812 let header = format!("[{}{}]", length_marker, arr.len());
813 writer
814 .write_all(header.as_bytes())
815 .map_err(|e| Error::Io(e.to_string()))?;
816 writer
817 .write_all(b"{")
818 .map_err(|e| Error::Io(e.to_string()))?;
819 let keys_str = keys.join(&options.get_delimiter().to_string());
820 writer
821 .write_all(keys_str.as_bytes())
822 .map_err(|e| Error::Io(e.to_string()))?;
823 writer
824 .write_all(b"}:\n")
825 .map_err(|e| Error::Io(e.to_string()))?;
826 encode_tabular_array_rows_to_writer(arr, keys, writer, indent_level, options)?;
828 } else if arr.iter().all(is_primitive) {
829 let length_marker = options
831 .length_marker
832 .map(|m| format!("{m}"))
833 .unwrap_or_default();
834 let header = format!("[{}{}]:", length_marker, arr.len());
835 writer
836 .write_all(header.as_bytes())
837 .map_err(|e| Error::Io(e.to_string()))?;
838 let delimiter = options.get_delimiter();
839 let mut first = true;
840 for item in arr {
841 if !first {
842 let delim_bytes = [delimiter as u8];
843 writer
844 .write_all(&delim_bytes)
845 .map_err(|e| Error::Io(e.to_string()))?;
846 }
847 encode_primitive_value_to_writer(item, writer, delimiter)?;
848 first = false;
849 }
850 } else {
851 let length_marker = options
853 .length_marker
854 .map(|m| format!("{m}"))
855 .unwrap_or_default();
856 let header = format!("[{}{}]:", length_marker, arr.len());
857 writer
858 .write_all(header.as_bytes())
859 .map_err(|e| Error::Io(e.to_string()))?;
860 writer
861 .write_all(b"\n")
862 .map_err(|e| Error::Io(e.to_string()))?;
863 encode_list_array_to_writer(arr, writer, indent_level, options)?;
864 }
865 }
866 Value::Object(_) => {
867 writer
868 .write_all(b": ")
869 .map_err(|e| Error::Io(e.to_string()))?;
870 writer
871 .write_all(b"\n")
872 .map_err(|e| Error::Io(e.to_string()))?;
873 encode_value_to_writer(value, writer, indent_level + 1, options)?;
874 }
875 _ => {
876 writer
877 .write_all(b": ")
878 .map_err(|e| Error::Io(e.to_string()))?;
879 encode_value_to_writer(value, writer, indent_level, options)?;
880 }
881 }
882 first = false;
883 }
884
885 Ok(())
886}