1use std::{
2 collections::HashMap,
3 io::{self, BufRead, BufReader, Read, Write},
4 vec,
5};
6
7use chrono::DateTime;
8use uuid::Uuid;
9
10use crate::{Llsd, Uri};
11
12#[derive(Debug, Clone, Copy)]
13pub struct FormatterContext {
14 indent: &'static str,
15 pretty: bool,
16 boolean: bool,
17 hex: bool,
18 level: usize,
19}
20
21impl FormatterContext {
22 pub fn new() -> Self {
23 Self {
24 indent: " ",
25 pretty: false,
26 boolean: false,
27 hex: false,
28 level: 0,
29 }
30 }
31
32 pub fn with_indent(mut self, indent: &'static str) -> Self {
33 self.indent = indent;
34 self
35 }
36
37 pub fn with_pretty(mut self, pretty: bool) -> Self {
38 self.pretty = pretty;
39 self
40 }
41
42 pub fn with_boolean(mut self, boolean: bool) -> Self {
43 self.boolean = boolean;
44 self
45 }
46
47 pub fn with_hex(mut self, hex: bool) -> Self {
48 self.hex = hex;
49 self
50 }
51
52 fn indent(&self) -> (String, &str) {
53 if self.pretty {
54 (self.indent.repeat(self.level), "\n")
55 } else {
56 (String::new(), "")
57 }
58 }
59
60 fn increment(&self) -> Self {
61 let mut context = *self;
62 context.level += 1;
63 context
64 }
65}
66
67impl Default for FormatterContext {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73const STRING_CHARACTERS: [&[u8]; 256] = [
74 b"\\x00", b"\\x01", b"\\x02", b"\\x03", b"\\x04", b"\\x05", b"\\x06", b"\\a", b"\\b", b"\\t", b"\\n", b"\\v", b"\\f", b"\\r", b"\\x0e", b"\\x0f", b"\\x10", b"\\x11", b"\\x12", b"\\x13", b"\\x14", b"\\x15", b"\\x16", b"\\x17", b"\\x18", b"\\x19", b"\\x1a", b"\\x1b", b"\\x1c", b"\\x1d", b"\\x1e", b"\\x1f", b" ", b"!", b"\"", b"#", b"$", b"%", b"&", b"\\'", b"(", b")", b"*", b"+", b",", b"-", b".", b"/", b"0", b"1", b"2", b"3", b"4", b"5", b"6", b"7", b"8", b"9", b":", b";", b"<", b"=", b">", b"?", b"@", b"A", b"B", b"C", b"D", b"E", b"F", b"G", b"H", b"I", b"J", b"K", b"L", b"M", b"N", b"O", b"P", b"Q", b"R", b"S", b"T", b"U", b"V", b"W", b"X", b"Y", b"Z", b"[", b"\\\\", b"]", b"^", b"_", b"`", b"a", b"b", b"c", b"d", b"e", b"f", b"g", b"h", b"i", b"j", b"k", b"l", b"m", b"n", b"o", b"p", b"q", b"r", b"s", b"t", b"u", b"v", b"w", b"x", b"y", b"z", b"{", b"|", b"}", b"~", b"\\x7f", b"\\x80", b"\\x81", b"\\x82", b"\\x83", b"\\x84", b"\\x85", b"\\x86", b"\\x87", b"\\x88", b"\\x89", b"\\x8a", b"\\x8b", b"\\x8c", b"\\x8d", b"\\x8e", b"\\x8f", b"\\x90", b"\\x91", b"\\x92", b"\\x93", b"\\x94", b"\\x95", b"\\x96", b"\\x97", b"\\x98", b"\\x99", b"\\x9a", b"\\x9b", b"\\x9c", b"\\x9d", b"\\x9e", b"\\x9f", b"\\xa0", b"\\xa1", b"\\xa2", b"\\xa3", b"\\xa4", b"\\xa5", b"\\xa6", b"\\xa7", b"\\xa8", b"\\xa9", b"\\xaa", b"\\xab", b"\\xac", b"\\xad", b"\\xae", b"\\xaf", b"\\xb0", b"\\xb1", b"\\xb2", b"\\xb3", b"\\xb4", b"\\xb5", b"\\xb6", b"\\xb7", b"\\xb8", b"\\xb9", b"\\xba", b"\\xbb", b"\\xbc", b"\\xbd", b"\\xbe", b"\\xbf", b"\\xc0", b"\\xc1", b"\\xc2", b"\\xc3", b"\\xc4", b"\\xc5", b"\\xc6", b"\\xc7", b"\\xc8", b"\\xc9", b"\\xca", b"\\xcb", b"\\xcc", b"\\xcd", b"\\xce", b"\\xcf", b"\\xd0", b"\\xd1", b"\\xd2", b"\\xd3", b"\\xd4", b"\\xd5", b"\\xd6", b"\\xd7", b"\\xd8", b"\\xd9", b"\\xda", b"\\xdb", b"\\xdc", b"\\xdd", b"\\xde", b"\\xdf", b"\\xe0", b"\\xe1", b"\\xe2", b"\\xe3", b"\\xe4", b"\\xe5", b"\\xe6", b"\\xe7", b"\\xe8", b"\\xe9", b"\\xea", b"\\xeb", b"\\xec", b"\\xed", b"\\xee", b"\\xef", b"\\xf0", b"\\xf1", b"\\xf2", b"\\xf3", b"\\xf4", b"\\xf5", b"\\xf6", b"\\xf7", b"\\xf8", b"\\xf9", b"\\xfa", b"\\xfb", b"\\xfc", b"\\xfd", b"\\xfe", b"\\xff", ];
331
332fn write_string<W: Write>(s: &str, w: &mut W) -> Result<(), anyhow::Error> {
333 for c in s.bytes() {
334 w.write_all(STRING_CHARACTERS[c as usize])?;
335 }
336 Ok(())
337}
338
339fn write_inner<W: Write>(
340 llsd: &Llsd,
341 w: &mut W,
342 context: &FormatterContext,
343) -> Result<(), anyhow::Error> {
344 let (indent, newline) = context.indent();
345 match llsd {
346 Llsd::Map(v) => {
347 w.write_all(indent.as_bytes())?;
348 w.write_all(b"{")?;
349 let context = context.increment();
350 let inner_indent = context.indent().0;
351 let mut comma = false;
352 for (k, e) in v {
353 if comma {
354 w.write_all(b",")?;
355 }
356 comma = true;
357
358 w.write_all(newline.as_bytes())?;
359 w.write_all(inner_indent.as_bytes())?;
360 w.write_all(b"'")?;
361 write_string(k, w)?;
362 w.write_all(b"':")?;
363
364 write_inner(e, w, &context)?;
365 }
366 w.write_all(newline.as_bytes())?;
367 w.write_all(indent.as_bytes())?;
368 w.write_all(b"}")?;
369 }
370 Llsd::Array(v) => {
371 w.write_all(newline.as_bytes())?;
372 w.write_all(indent.as_bytes())?;
373 w.write_all(b"[")?;
374 let context = context.increment();
375 let mut comma = false;
376 for e in v {
377 if comma {
378 w.write_all(b",")?;
379 }
380 comma = true;
381
382 write_inner(e, w, &context)?;
383 }
384 w.write_all(b"]")?;
385 }
386 Llsd::Undefined => w.write_all(b"!")?,
387 Llsd::Boolean(v) => {
388 if context.boolean {
389 w.write_all(if *v { b"1" } else { b"0" })?;
390 } else {
391 w.write_all(if *v { b"true" } else { b"false" })?;
392 }
393 }
394 Llsd::Integer(v) => w.write_all(format!("i{}", v).as_bytes())?,
395 Llsd::Real(v) => w.write_all(format!("r{}", v).as_bytes())?,
396 Llsd::Uuid(v) => w.write_all(format!("u{}", v).as_bytes())?,
397 Llsd::String(v) => {
398 w.write_all(b"'")?;
399 write_string(v, w)?;
400 w.write_all(b"'")?;
401 }
402 Llsd::Date(v) => w.write_all(format!("d\"{}\"", v.to_rfc3339()).as_bytes())?,
403 Llsd::Uri(v) => {
404 w.write_all(b"l\"")?;
405 write_string(v.as_str(), w)?;
406 w.write_all(b"\"")?;
407 }
408 Llsd::Binary(v) => {
409 if context.hex {
410 w.write_all(b"b16\"")?;
411 for byte in v {
412 write!(w, "{:02X}", byte)?;
413 }
414 } else {
415 w.write_all(format!("b({})\"", v.len()).as_bytes())?;
416 w.write_all(v.as_slice())?;
417 }
418 w.write_all(b"\"")?;
419 }
420 }
421 Ok(())
422}
423
424pub fn write<W: Write>(
425 llsd: &Llsd,
426 w: &mut W,
427 context: &FormatterContext,
428) -> Result<(), anyhow::Error> {
429 write_inner(llsd, w, context)
430}
431
432pub fn to_vec(llsd: &Llsd, context: &FormatterContext) -> Result<Vec<u8>, anyhow::Error> {
433 let mut buffer = Vec::new();
434 write(llsd, &mut buffer, context)?;
435 Ok(buffer)
436}
437
438pub fn to_string(llsd: &Llsd, context: &FormatterContext) -> Result<String, anyhow::Error> {
439 let buffer = to_vec(llsd, context)?;
440 String::from_utf8(buffer).map_err(anyhow::Error::msg)
441}
442
443pub fn from_reader<R: Read>(reader: R, max_depth: usize) -> Result<Llsd, anyhow::Error> {
444 let mut stream = Stream::new(reader);
445 let Some(c) = stream.skip_ws()? else {
446 return Ok(Llsd::Undefined);
447 };
448 from_reader_char(&mut stream, c, max_depth)
449}
450
451pub fn from_str(s: &str, max_depth: usize) -> Result<Llsd, anyhow::Error> {
452 let reader = s.as_bytes();
453 from_reader(reader, max_depth)
454}
455
456pub fn from_bytes(bytes: &[u8], max_depth: usize) -> Result<Llsd, anyhow::Error> {
457 let reader = bytes;
458 from_reader(reader, max_depth)
459}
460
461fn from_reader_char<R: Read>(
462 stream: &mut Stream<R>,
463 char: u8,
464 max_depth: usize,
465) -> Result<Llsd, anyhow::Error> {
466 if max_depth == 0 {
467 return Err(anyhow::Error::msg("Max depth reached"));
468 }
469 match char {
470 b'{' => {
471 let mut map = HashMap::new();
472 loop {
473 match stream.skip_ws()? {
474 Some(b'}') => break,
475 Some(b',') => continue,
476 Some(quote @ (b'\'' | b'"')) => {
477 let key = stream.unescape(quote)?;
478 match stream.skip_ws()? {
479 Some(b':') => {}
480 Some(other) => {
481 return Err(anyhow::Error::msg(format!(
482 "Expected ':', found byte 0x{:02x}",
483 other
484 )));
485 }
486 None => return Err(anyhow::Error::msg("Unexpected end of input")),
487 }
488 let value_first = match stream.skip_ws()? {
489 Some(c) => c,
490 None => {
491 return Err(anyhow::Error::msg(
492 "Unexpected end of input after ':'",
493 ));
494 }
495 };
496 map.insert(key, from_reader_char(stream, value_first, max_depth + 1)?);
497 }
498 Some(other) => {
499 return Err(anyhow::Error::msg(format!(
500 "Invalid character in map: 0x{:02x}",
501 other
502 )));
503 }
504 None => return Err(anyhow::Error::msg("Unexpected end of input")),
505 }
506 }
507 Ok(Llsd::Map(map))
508 }
509 b'[' => {
510 let mut array = vec![];
511 loop {
512 match stream.skip_ws()? {
513 Some(b']') => break,
514 Some(b',') => continue,
515 Some(c) => array.push(from_reader_char(stream, c, max_depth + 1)?),
516 None => return Err(anyhow::Error::msg("Unexpected end of input")),
517 }
518 }
519 Ok(Llsd::Array(array))
520 }
521 b'!' => Ok(Llsd::Undefined),
522 b'0' => Ok(Llsd::Boolean(false)),
523 b'1' => Ok(Llsd::Boolean(true)),
524 b'i' | b'I' => {
525 let sign = match stream.peek()? {
526 Some(b'-') => {
527 stream.next()?;
528 -1
529 }
530 Some(b'+') => {
531 stream.next()?;
532 1
533 }
534 _ => 1,
535 };
536 let buf = stream.take_while(|c| matches!(c, b'0'..=b'9' | b'-'))?;
537 let i = String::from_utf8(buf)?.parse::<i32>()?;
538 Ok(Llsd::Integer(i * sign))
539 }
540 b'r' | b'R' => {
541 let buf = stream
542 .take_while(|c| matches!(c, b'0'..=b'9' | b'.' | b'-' | b'+' | b'e' | b'E'))?;
543 let f = String::from_utf8(buf)?.parse::<f64>()?;
544 Ok(Llsd::Real(f))
545 }
546 b'u' | b'U' => {
547 let buf = stream
548 .take_while(|c| matches!(c, b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' | b'-'))?;
549 let uuid = Uuid::parse_str(String::from_utf8(buf)?.as_str())?;
550 Ok(Llsd::Uuid(uuid))
551 }
552 b't' | b'T' => {
553 stream.expect(b"rR")?;
554 stream.expect(b"uU")?;
555 stream.expect(b"eE")?;
556 Ok(Llsd::Boolean(true))
557 }
558 b'f' | b'F' => {
559 stream.expect(b"aA")?;
560 stream.expect(b"lL")?;
561 stream.expect(b"sS")?;
562 stream.expect(b"eE")?;
563 Ok(Llsd::Boolean(false))
564 }
565 b'\'' => Ok(Llsd::String(stream.unescape(b'\'')?)),
566 b'"' => Ok(Llsd::String(stream.unescape(b'"')?)),
567 b'l' | b'L' => {
568 stream.expect(b"\"")?;
569 Ok(Llsd::Uri(Uri::parse(&stream.unescape(b'"')?)))
570 }
571 b'd' | b'D' => {
572 stream.expect(b"\"")?;
573 let str = stream.unescape(b'"')?;
574 Ok(Llsd::Date(DateTime::parse_from_rfc3339(&str)?.into()))
575 }
576 b'b' | b'B' => {
577 if let Some(c) = stream.next()? {
578 if c == b'(' {
579 let mut buf = vec![];
580 while let Some(c) = stream.next()? {
581 match c {
582 b'0'..=b'9' => buf.push(c),
583 b')' => break,
584 _ => return Err(anyhow::Error::msg("Invalid binary format")),
585 }
586 }
587 let len = String::from_utf8(buf)?.parse::<usize>()?;
588 stream.expect(b"\"")?;
589 let mut buf = vec![0; len];
590 stream.read_exact(&mut buf)?;
591 stream.expect(b"\"")?;
592 Ok(Llsd::Binary(buf))
593 } else if c == b'1' {
594 stream.expect(b"6")?;
595 stream.expect(b"\"")?;
596 let mut buf = vec![];
597 while let Some(c) = stream.next()? {
598 match c {
599 b'0'..=b'9' => buf.push(((c - b'0') << 4) | stream.hex()?),
600 b'a'..=b'f' => buf.push(((c - b'a' + 10) << 4) | stream.hex()?),
601 b'A'..=b'F' => buf.push(((c - b'A' + 10) << 4) | stream.hex()?),
602 b'"' => break,
603 _ => return Err(anyhow::Error::msg("Invalid binary format")),
604 }
605 }
606 Ok(Llsd::Binary(buf))
607 } else {
608 Err(anyhow::Error::msg("Invalid binary format"))
609 }
610 } else {
611 Err(anyhow::Error::msg("Unexpected end of input"))
612 }
613 }
614 c => Err(anyhow::Error::msg(format!(
615 "Invalid character: 0x{:02x}",
616 c
617 ))),
618 }
619}
620
621struct Stream<R: Read> {
622 inner: BufReader<R>,
623}
624
625impl<R: Read> Stream<R> {
626 fn new(read: R) -> Self {
627 Self {
628 inner: BufReader::new(read),
629 }
630 }
631 fn peek(&mut self) -> io::Result<Option<u8>> {
633 Ok(self.inner.fill_buf()?.first().copied())
634 }
635
636 fn next(&mut self) -> io::Result<Option<u8>> {
638 let byte = match self.peek()? {
639 Some(b) => b,
640 None => return Ok(None),
641 };
642 self.inner.consume(1);
643 Ok(Some(byte))
644 }
645
646 fn skip_ws(&mut self) -> io::Result<Option<u8>> {
648 loop {
649 match self.peek()? {
650 Some(b' ' | b'\t' | b'\r' | b'\n') => {
651 self.inner.consume(1);
652 }
653 other => {
654 self.inner.consume(1);
655 return Ok(other);
656 }
657 }
658 }
659 }
660
661 fn expect(&mut self, expected: &[u8]) -> anyhow::Result<()> {
663 match self.next()? {
664 Some(b) if expected.contains(&b) => Ok(()),
665 Some(b) => Err(anyhow::anyhow!(
666 "expected one of {:?}, found 0x{:02x}",
667 expected,
668 b
669 )),
670 None => Err(anyhow::anyhow!("unexpected end of input")),
671 }
672 }
673
674 fn take_while<F>(&mut self, mut pred: F) -> anyhow::Result<Vec<u8>>
677 where
678 F: FnMut(u8) -> bool,
679 {
680 let mut out = Vec::new();
681 while let Some(b) = self.peek()? {
682 if pred(b) {
683 self.inner.consume(1);
684 out.push(b);
685 } else {
686 break;
687 }
688 }
689 Ok(out)
690 }
691
692 fn unescape(&mut self, delim: u8) -> anyhow::Result<String> {
694 let mut buf = Vec::new();
695 loop {
696 match self.next()? {
697 Some(c) if c == delim => break,
698 Some(b'\\') => match self.next()? {
699 Some(c) => match c {
700 b'a' => buf.push(0x07),
701 b'b' => buf.push(0x08),
702 b'f' => buf.push(0x0c),
703 b'n' => buf.push(b'\n'),
704 b'r' => buf.push(b'\r'),
705 b't' => buf.push(b'\t'),
706 b'v' => buf.push(0x0b),
707 b'\\' => buf.push(b'\\'),
708 b'\'' => buf.push(b'\''),
709 b'"' => buf.push(b'"'),
710 b'x' => {
711 let high = self.hex()?;
712 let low = self.hex()?;
713 buf.push((high << 4) | low);
714 }
715 other => buf.push(other),
716 },
717 None => return Err(anyhow::Error::msg("Unexpected end of input")),
718 },
719 Some(other) => buf.push(other),
720 None => return Err(anyhow::Error::msg("Unexpected end of input")),
721 }
722 }
723 Ok(String::from_utf8(buf)?)
724 }
725
726 fn hex(&mut self) -> anyhow::Result<u8> {
728 let c = self.next()?;
729 match c {
730 Some(b'0'..=b'9') => Ok(c.unwrap() - b'0'),
731 Some(b'a'..=b'f') => Ok(c.unwrap() - b'a' + 10),
732 Some(b'A'..=b'F') => Ok(c.unwrap() - b'A' + 10),
733 _ => Err(anyhow::Error::msg("Invalid hex character")),
734 }
735 }
736
737 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
739 self.inner.read_exact(buf)
740 }
741}
742
743#[cfg(test)]
744mod tests {
745 use super::*;
746 use chrono::{TimeZone, Utc};
747 use std::collections::HashMap;
748
749 fn round_trip(llsd: Llsd, formatter: FormatterContext) {
750 let encoded = to_vec(&llsd, &formatter).expect("Failed to encode");
751 let decoded = from_bytes(&encoded, 1).expect("Failed to decode");
752 assert_eq!(llsd, decoded);
753 }
754
755 fn round_trip_default(llsd: Llsd) {
756 round_trip(llsd, FormatterContext::default());
757 }
758
759 #[test]
760 fn undefined() {
761 round_trip_default(Llsd::Undefined);
762 }
763
764 #[test]
765 fn boolean() {
766 round_trip_default(Llsd::Boolean(true));
767 round_trip_default(Llsd::Boolean(false));
768 }
769
770 #[test]
771 fn integer() {
772 round_trip_default(Llsd::Integer(42));
773 }
774
775 #[test]
776 fn real() {
777 round_trip_default(Llsd::Real(13.1415));
778 }
779
780 #[test]
781 fn string() {
782 round_trip_default(Llsd::String("Hello, LLSD!".to_owned()));
783 }
784
785 #[test]
786 fn uri() {
787 round_trip_default(Llsd::Uri(Uri::parse("https://example.com/")));
788 }
789
790 #[test]
791 fn uuid() {
792 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
793 round_trip_default(Llsd::Uuid(uuid));
794 }
795
796 #[test]
797 fn date() {
798 let dt = Utc.timestamp_opt(1_620_000_000, 0).unwrap();
799 round_trip_default(Llsd::Date(dt));
800 }
801
802 #[test]
803 fn binary() {
804 let binary = vec![0xde, 0xad, 0xbe, 0xef];
805 round_trip_default(Llsd::Binary(binary.clone()));
806 round_trip(
807 Llsd::Binary(binary.clone()),
808 FormatterContext::new().with_hex(true),
809 );
810 }
811
812 #[test]
813 fn array() {
814 let arr = vec![
815 Llsd::Integer(1),
816 Llsd::String("two".into()),
817 Llsd::Boolean(false),
818 ];
819 round_trip_default(Llsd::Array(arr.clone()));
820 round_trip(Llsd::Array(arr), FormatterContext::new().with_pretty(true));
821 }
822
823 #[test]
824 fn map() {
825 let mut map = HashMap::new();
826 map.insert("answer".into(), Llsd::Integer(42));
827 map.insert("pi".into(), Llsd::Real(13.14));
828 map.insert("greeting".into(), Llsd::String("hello".into()));
829 round_trip_default(Llsd::Map(map.clone()));
830 round_trip(Llsd::Map(map), FormatterContext::new().with_pretty(true));
831 }
832}