llsd_rs/
notation.rs

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", // 0
75    b"\\x01", // 1
76    b"\\x02", // 2
77    b"\\x03", // 3
78    b"\\x04", // 4
79    b"\\x05", // 5
80    b"\\x06", // 6
81    b"\\a",   // 7
82    b"\\b",   // 8
83    b"\\t",   // 9
84    b"\\n",   // 10
85    b"\\v",   // 11
86    b"\\f",   // 12
87    b"\\r",   // 13
88    b"\\x0e", // 14
89    b"\\x0f", // 15
90    b"\\x10", // 16
91    b"\\x11", // 17
92    b"\\x12", // 18
93    b"\\x13", // 19
94    b"\\x14", // 20
95    b"\\x15", // 21
96    b"\\x16", // 22
97    b"\\x17", // 23
98    b"\\x18", // 24
99    b"\\x19", // 25
100    b"\\x1a", // 26
101    b"\\x1b", // 27
102    b"\\x1c", // 28
103    b"\\x1d", // 29
104    b"\\x1e", // 30
105    b"\\x1f", // 31
106    b" ",     // 32
107    b"!",     // 33
108    b"\"",    // 34
109    b"#",     // 35
110    b"$",     // 36
111    b"%",     // 37
112    b"&",     // 38
113    b"\\'",   // 39
114    b"(",     // 40
115    b")",     // 41
116    b"*",     // 42
117    b"+",     // 43
118    b",",     // 44
119    b"-",     // 45
120    b".",     // 46
121    b"/",     // 47
122    b"0",     // 48
123    b"1",     // 49
124    b"2",     // 50
125    b"3",     // 51
126    b"4",     // 52
127    b"5",     // 53
128    b"6",     // 54
129    b"7",     // 55
130    b"8",     // 56
131    b"9",     // 57
132    b":",     // 58
133    b";",     // 59
134    b"<",     // 60
135    b"=",     // 61
136    b">",     // 62
137    b"?",     // 63
138    b"@",     // 64
139    b"A",     // 65
140    b"B",     // 66
141    b"C",     // 67
142    b"D",     // 68
143    b"E",     // 69
144    b"F",     // 70
145    b"G",     // 71
146    b"H",     // 72
147    b"I",     // 73
148    b"J",     // 74
149    b"K",     // 75
150    b"L",     // 76
151    b"M",     // 77
152    b"N",     // 78
153    b"O",     // 79
154    b"P",     // 80
155    b"Q",     // 81
156    b"R",     // 82
157    b"S",     // 83
158    b"T",     // 84
159    b"U",     // 85
160    b"V",     // 86
161    b"W",     // 87
162    b"X",     // 88
163    b"Y",     // 89
164    b"Z",     // 90
165    b"[",     // 91
166    b"\\\\",  // 92
167    b"]",     // 93
168    b"^",     // 94
169    b"_",     // 95
170    b"`",     // 96
171    b"a",     // 97
172    b"b",     // 98
173    b"c",     // 99
174    b"d",     // 100
175    b"e",     // 101
176    b"f",     // 102
177    b"g",     // 103
178    b"h",     // 104
179    b"i",     // 105
180    b"j",     // 106
181    b"k",     // 107
182    b"l",     // 108
183    b"m",     // 109
184    b"n",     // 110
185    b"o",     // 111
186    b"p",     // 112
187    b"q",     // 113
188    b"r",     // 114
189    b"s",     // 115
190    b"t",     // 116
191    b"u",     // 117
192    b"v",     // 118
193    b"w",     // 119
194    b"x",     // 120
195    b"y",     // 121
196    b"z",     // 122
197    b"{",     // 123
198    b"|",     // 124
199    b"}",     // 125
200    b"~",     // 126
201    b"\\x7f", // 127
202    b"\\x80", // 128
203    b"\\x81", // 129
204    b"\\x82", // 130
205    b"\\x83", // 131
206    b"\\x84", // 132
207    b"\\x85", // 133
208    b"\\x86", // 134
209    b"\\x87", // 135
210    b"\\x88", // 136
211    b"\\x89", // 137
212    b"\\x8a", // 138
213    b"\\x8b", // 139
214    b"\\x8c", // 140
215    b"\\x8d", // 141
216    b"\\x8e", // 142
217    b"\\x8f", // 143
218    b"\\x90", // 144
219    b"\\x91", // 145
220    b"\\x92", // 146
221    b"\\x93", // 147
222    b"\\x94", // 148
223    b"\\x95", // 149
224    b"\\x96", // 150
225    b"\\x97", // 151
226    b"\\x98", // 152
227    b"\\x99", // 153
228    b"\\x9a", // 154
229    b"\\x9b", // 155
230    b"\\x9c", // 156
231    b"\\x9d", // 157
232    b"\\x9e", // 158
233    b"\\x9f", // 159
234    b"\\xa0", // 160
235    b"\\xa1", // 161
236    b"\\xa2", // 162
237    b"\\xa3", // 163
238    b"\\xa4", // 164
239    b"\\xa5", // 165
240    b"\\xa6", // 166
241    b"\\xa7", // 167
242    b"\\xa8", // 168
243    b"\\xa9", // 169
244    b"\\xaa", // 170
245    b"\\xab", // 171
246    b"\\xac", // 172
247    b"\\xad", // 173
248    b"\\xae", // 174
249    b"\\xaf", // 175
250    b"\\xb0", // 176
251    b"\\xb1", // 177
252    b"\\xb2", // 178
253    b"\\xb3", // 179
254    b"\\xb4", // 180
255    b"\\xb5", // 181
256    b"\\xb6", // 182
257    b"\\xb7", // 183
258    b"\\xb8", // 184
259    b"\\xb9", // 185
260    b"\\xba", // 186
261    b"\\xbb", // 187
262    b"\\xbc", // 188
263    b"\\xbd", // 189
264    b"\\xbe", // 190
265    b"\\xbf", // 191
266    b"\\xc0", // 192
267    b"\\xc1", // 193
268    b"\\xc2", // 194
269    b"\\xc3", // 195
270    b"\\xc4", // 196
271    b"\\xc5", // 197
272    b"\\xc6", // 198
273    b"\\xc7", // 199
274    b"\\xc8", // 200
275    b"\\xc9", // 201
276    b"\\xca", // 202
277    b"\\xcb", // 203
278    b"\\xcc", // 204
279    b"\\xcd", // 205
280    b"\\xce", // 206
281    b"\\xcf", // 207
282    b"\\xd0", // 208
283    b"\\xd1", // 209
284    b"\\xd2", // 210
285    b"\\xd3", // 211
286    b"\\xd4", // 212
287    b"\\xd5", // 213
288    b"\\xd6", // 214
289    b"\\xd7", // 215
290    b"\\xd8", // 216
291    b"\\xd9", // 217
292    b"\\xda", // 218
293    b"\\xdb", // 219
294    b"\\xdc", // 220
295    b"\\xdd", // 221
296    b"\\xde", // 222
297    b"\\xdf", // 223
298    b"\\xe0", // 224
299    b"\\xe1", // 225
300    b"\\xe2", // 226
301    b"\\xe3", // 227
302    b"\\xe4", // 228
303    b"\\xe5", // 229
304    b"\\xe6", // 230
305    b"\\xe7", // 231
306    b"\\xe8", // 232
307    b"\\xe9", // 233
308    b"\\xea", // 234
309    b"\\xeb", // 235
310    b"\\xec", // 236
311    b"\\xed", // 237
312    b"\\xee", // 238
313    b"\\xef", // 239
314    b"\\xf0", // 240
315    b"\\xf1", // 241
316    b"\\xf2", // 242
317    b"\\xf3", // 243
318    b"\\xf4", // 244
319    b"\\xf5", // 245
320    b"\\xf6", // 246
321    b"\\xf7", // 247
322    b"\\xf8", // 248
323    b"\\xf9", // 249
324    b"\\xfa", // 250
325    b"\\xfb", // 251
326    b"\\xfc", // 252
327    b"\\xfd", // 253
328    b"\\xfe", // 254
329    b"\\xff", // 255
330];
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    /// Return the next byte **without** consuming it.
632    fn peek(&mut self) -> io::Result<Option<u8>> {
633        Ok(self.inner.fill_buf()?.first().copied())
634    }
635
636    /// Consume one byte and return it.
637    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    /// Skip ASCII whitespace and return the first non-WS byte, consuming it
647    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    /// Consume one of the expected bytes.
662    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    /// Read a sequence that satisfies `pred` (stop *before* the first byte
675    /// that fails the predicate).
676    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    /// Unescape a string until the delimiter is reached.
693    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    /// Read a hex character and return its value.
727    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    /// Read exactly `n` bytes into the buffer.
738    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}