Skip to main content

yaml_rt_serde/
ser.rs

1use std::io::Write;
2
3use serde::{Serialize, ser};
4
5use crate::value::{Mapping, Number, Tag, TaggedValue, Value};
6use crate::{Error, Result};
7
8/// Serializes a value to a UTF-8 YAML string.
9///
10/// # Errors
11///
12/// Returns an error when `value` cannot be represented as YAML.
13pub fn to_string<T>(value: &T) -> Result<String>
14where
15    T: ?Sized + Serialize,
16{
17    let mut output = Vec::new();
18    to_writer(&mut output, value)?;
19    String::from_utf8(output).map_err(|error| Error::message(error.to_string()))
20}
21
22/// Serializes a value as one YAML document.
23///
24/// # Errors
25///
26/// Returns an error when serialization or writing to `writer` fails.
27pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
28where
29    W: Write,
30    T: ?Sized + Serialize,
31{
32    let mut serializer = Serializer::new(writer);
33    value.serialize(&mut serializer)
34}
35
36/// A YAML serializer writing one or more documents to an `io::Write` sink.
37pub struct Serializer<W> {
38    writer: W,
39    documents: usize,
40}
41
42impl<W> Serializer<W>
43where
44    W: Write,
45{
46    /// Creates a serializer around `writer`.
47    pub const fn new(writer: W) -> Self {
48        Self {
49            writer,
50            documents: 0,
51        }
52    }
53
54    /// Flushes the underlying writer.
55    ///
56    /// # Errors
57    ///
58    /// Returns an error when the underlying writer cannot be flushed.
59    pub fn flush(&mut self) -> Result<()> {
60        self.writer.flush().map_err(Error::io)
61    }
62
63    /// Flushes and returns the underlying writer.
64    ///
65    /// # Errors
66    ///
67    /// Returns an error when the underlying writer cannot be flushed.
68    pub fn into_inner(mut self) -> Result<W> {
69        self.flush()?;
70        Ok(self.writer)
71    }
72
73    fn write_document(&mut self, value: &Value) -> Result<()> {
74        if self.documents > 0 {
75            self.writer.write_all(b"---\n").map_err(Error::io)?;
76        }
77        let mut output = String::new();
78        render_value(value, 0, &mut output);
79        if !output.ends_with('\n') {
80            output.push('\n');
81        }
82        self.writer
83            .write_all(output.as_bytes())
84            .map_err(Error::io)?;
85        self.documents += 1;
86        Ok(())
87    }
88
89    fn collect<T>(&mut self, value: &T) -> Result<()>
90    where
91        T: ?Sized + Serialize,
92    {
93        let value = crate::to_value(value)?;
94        self.write_document(&value)
95    }
96}
97
98pub struct ValueSequence {
99    values: Vec<Value>,
100    tag: Option<String>,
101}
102
103impl ValueSequence {
104    fn new(len: Option<usize>, tag: Option<String>) -> Self {
105        Self {
106            values: Vec::with_capacity(len.unwrap_or(0)),
107            tag,
108        }
109    }
110
111    fn push<T>(&mut self, value: &T) -> Result<()>
112    where
113        T: ?Sized + Serialize,
114    {
115        self.values.push(crate::to_value(value)?);
116        Ok(())
117    }
118
119    fn finish(self) -> Value {
120        let value = Value::Sequence(self.values);
121        match self.tag {
122            Some(tag) => Value::Tagged(Box::new(TaggedValue {
123                tag: Tag::new(tag),
124                value,
125            })),
126            None => value,
127        }
128    }
129}
130
131impl ser::SerializeSeq for ValueSequence {
132    type Ok = Value;
133    type Error = Error;
134    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
135    where
136        T: ?Sized + Serialize,
137    {
138        self.push(value)
139    }
140    fn end(self) -> Result<Value> {
141        Ok(self.finish())
142    }
143}
144impl ser::SerializeTuple for ValueSequence {
145    type Ok = Value;
146    type Error = Error;
147    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
148    where
149        T: ?Sized + Serialize,
150    {
151        self.push(value)
152    }
153    fn end(self) -> Result<Value> {
154        Ok(self.finish())
155    }
156}
157impl ser::SerializeTupleStruct for ValueSequence {
158    type Ok = Value;
159    type Error = Error;
160    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
161    where
162        T: ?Sized + Serialize,
163    {
164        self.push(value)
165    }
166    fn end(self) -> Result<Value> {
167        Ok(self.finish())
168    }
169}
170impl ser::SerializeTupleVariant for ValueSequence {
171    type Ok = Value;
172    type Error = Error;
173    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
174    where
175        T: ?Sized + Serialize,
176    {
177        self.push(value)
178    }
179    fn end(self) -> Result<Value> {
180        Ok(self.finish())
181    }
182}
183
184pub struct ValueMapping {
185    entries: Mapping,
186    pending: Option<Value>,
187    tag: Option<String>,
188}
189
190impl ValueMapping {
191    fn new(len: Option<usize>, tag: Option<String>) -> Self {
192        Self {
193            entries: Mapping::with_capacity(len.unwrap_or(0)),
194            pending: None,
195            tag,
196        }
197    }
198
199    fn finish(self) -> Result<Value> {
200        if self.pending.is_some() {
201            return Err(Error::message("map ended before serializing a value"));
202        }
203        let value = Value::Mapping(self.entries);
204        Ok(match self.tag {
205            Some(tag) => Value::Tagged(Box::new(TaggedValue {
206                tag: Tag::new(tag),
207                value,
208            })),
209            None => value,
210        })
211    }
212}
213
214impl ser::SerializeMap for ValueMapping {
215    type Ok = Value;
216    type Error = Error;
217    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
218    where
219        T: ?Sized + Serialize,
220    {
221        if self.pending.is_some() {
222            return Err(Error::message("map key serialized before its value"));
223        }
224        self.pending = Some(crate::to_value(key)?);
225        Ok(())
226    }
227    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
228    where
229        T: ?Sized + Serialize,
230    {
231        let key = self
232            .pending
233            .take()
234            .ok_or_else(|| Error::message("map value serialized before its key"))?;
235        self.entries.insert(key, crate::to_value(value)?);
236        Ok(())
237    }
238    fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<()>
239    where
240        K: ?Sized + Serialize,
241        V: ?Sized + Serialize,
242    {
243        self.entries
244            .insert(crate::to_value(key)?, crate::to_value(value)?);
245        Ok(())
246    }
247    fn end(self) -> Result<Value> {
248        self.finish()
249    }
250}
251
252impl ser::SerializeStruct for ValueMapping {
253    type Ok = Value;
254    type Error = Error;
255    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
256    where
257        T: ?Sized + Serialize,
258    {
259        self.entries
260            .insert(Value::String(key.to_owned()), crate::to_value(value)?);
261        Ok(())
262    }
263    fn end(self) -> Result<Value> {
264        self.finish()
265    }
266}
267
268impl ser::SerializeStructVariant for ValueMapping {
269    type Ok = Value;
270    type Error = Error;
271    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
272    where
273        T: ?Sized + Serialize,
274    {
275        self.entries
276            .insert(Value::String(key.to_owned()), crate::to_value(value)?);
277        Ok(())
278    }
279    fn end(self) -> Result<Value> {
280        self.finish()
281    }
282}
283
284pub enum DocumentSequence<'a, W> {
285    Sequence {
286        serializer: &'a mut Serializer<W>,
287        values: ValueSequence,
288    },
289    Mapping {
290        serializer: &'a mut Serializer<W>,
291        values: ValueMapping,
292    },
293}
294
295impl<'a, W: Write> DocumentSequence<'a, W> {
296    fn sequence(
297        serializer: &'a mut Serializer<W>,
298        len: Option<usize>,
299        tag: Option<String>,
300    ) -> Self {
301        Self::Sequence {
302            serializer,
303            values: ValueSequence::new(len, tag),
304        }
305    }
306    fn mapping(serializer: &'a mut Serializer<W>, len: Option<usize>, tag: Option<String>) -> Self {
307        Self::Mapping {
308            serializer,
309            values: ValueMapping::new(len, tag),
310        }
311    }
312}
313
314impl<W: Write> ser::SerializeSeq for DocumentSequence<'_, W> {
315    type Ok = ();
316    type Error = Error;
317    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
318    where
319        T: ?Sized + Serialize,
320    {
321        match self {
322            Self::Sequence { values, .. } => values.push(value),
323            Self::Mapping { .. } => unreachable!(),
324        }
325    }
326    fn end(self) -> Result<()> {
327        match self {
328            Self::Sequence { serializer, values } => serializer.write_document(&values.finish()),
329            Self::Mapping { .. } => unreachable!(),
330        }
331    }
332}
333impl<W: Write> ser::SerializeTuple for DocumentSequence<'_, W> {
334    type Ok = ();
335    type Error = Error;
336    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
337    where
338        T: ?Sized + Serialize,
339    {
340        ser::SerializeSeq::serialize_element(self, value)
341    }
342    fn end(self) -> Result<()> {
343        ser::SerializeSeq::end(self)
344    }
345}
346impl<W: Write> ser::SerializeTupleStruct for DocumentSequence<'_, W> {
347    type Ok = ();
348    type Error = Error;
349    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
350    where
351        T: ?Sized + Serialize,
352    {
353        ser::SerializeSeq::serialize_element(self, value)
354    }
355    fn end(self) -> Result<()> {
356        ser::SerializeSeq::end(self)
357    }
358}
359impl<W: Write> ser::SerializeTupleVariant for DocumentSequence<'_, W> {
360    type Ok = ();
361    type Error = Error;
362    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
363    where
364        T: ?Sized + Serialize,
365    {
366        ser::SerializeSeq::serialize_element(self, value)
367    }
368    fn end(self) -> Result<()> {
369        ser::SerializeSeq::end(self)
370    }
371}
372impl<W: Write> ser::SerializeMap for DocumentSequence<'_, W> {
373    type Ok = ();
374    type Error = Error;
375    fn serialize_key<T>(&mut self, key: &T) -> Result<()>
376    where
377        T: ?Sized + Serialize,
378    {
379        match self {
380            Self::Mapping { values, .. } => ser::SerializeMap::serialize_key(values, key),
381            Self::Sequence { .. } => unreachable!(),
382        }
383    }
384    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
385    where
386        T: ?Sized + Serialize,
387    {
388        match self {
389            Self::Mapping { values, .. } => ser::SerializeMap::serialize_value(values, value),
390            Self::Sequence { .. } => unreachable!(),
391        }
392    }
393    fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<()>
394    where
395        K: ?Sized + Serialize,
396        V: ?Sized + Serialize,
397    {
398        match self {
399            Self::Mapping { values, .. } => ser::SerializeMap::serialize_entry(values, key, value),
400            Self::Sequence { .. } => unreachable!(),
401        }
402    }
403    fn end(self) -> Result<()> {
404        match self {
405            Self::Mapping { serializer, values } => serializer.write_document(&values.finish()?),
406            Self::Sequence { .. } => unreachable!(),
407        }
408    }
409}
410impl<W: Write> ser::SerializeStruct for DocumentSequence<'_, W> {
411    type Ok = ();
412    type Error = Error;
413    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
414    where
415        T: ?Sized + Serialize,
416    {
417        match self {
418            Self::Mapping { values, .. } => {
419                ser::SerializeStruct::serialize_field(values, key, value)
420            }
421            Self::Sequence { .. } => unreachable!(),
422        }
423    }
424    fn end(self) -> Result<()> {
425        ser::SerializeMap::end(self)
426    }
427}
428impl<W: Write> ser::SerializeStructVariant for DocumentSequence<'_, W> {
429    type Ok = ();
430    type Error = Error;
431    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
432    where
433        T: ?Sized + Serialize,
434    {
435        match self {
436            Self::Mapping { values, .. } => {
437                ser::SerializeStruct::serialize_field(values, key, value)
438            }
439            Self::Sequence { .. } => unreachable!(),
440        }
441    }
442    fn end(self) -> Result<()> {
443        ser::SerializeMap::end(self)
444    }
445}
446
447impl<'a, W> ser::Serializer for &'a mut Serializer<W>
448where
449    W: Write,
450{
451    type Ok = ();
452    type Error = Error;
453    type SerializeSeq = DocumentSequence<'a, W>;
454    type SerializeTuple = DocumentSequence<'a, W>;
455    type SerializeTupleStruct = DocumentSequence<'a, W>;
456    type SerializeTupleVariant = DocumentSequence<'a, W>;
457    type SerializeMap = DocumentSequence<'a, W>;
458    type SerializeStruct = DocumentSequence<'a, W>;
459    type SerializeStructVariant = DocumentSequence<'a, W>;
460
461    fn serialize_bool(self, value: bool) -> Result<()> {
462        self.write_document(&Value::Bool(value))
463    }
464    fn serialize_i8(self, value: i8) -> Result<()> {
465        self.serialize_i128(value.into())
466    }
467    fn serialize_i16(self, value: i16) -> Result<()> {
468        self.serialize_i128(value.into())
469    }
470    fn serialize_i32(self, value: i32) -> Result<()> {
471        self.serialize_i128(value.into())
472    }
473    fn serialize_i64(self, value: i64) -> Result<()> {
474        self.serialize_i128(value.into())
475    }
476    fn serialize_i128(self, value: i128) -> Result<()> {
477        self.write_document(&Value::Number(Number::from(value)))
478    }
479    fn serialize_u8(self, value: u8) -> Result<()> {
480        self.serialize_u128(value.into())
481    }
482    fn serialize_u16(self, value: u16) -> Result<()> {
483        self.serialize_u128(value.into())
484    }
485    fn serialize_u32(self, value: u32) -> Result<()> {
486        self.serialize_u128(value.into())
487    }
488    fn serialize_u64(self, value: u64) -> Result<()> {
489        self.serialize_u128(value.into())
490    }
491    fn serialize_u128(self, value: u128) -> Result<()> {
492        self.write_document(&Value::Number(Number::from(value)))
493    }
494    fn serialize_f32(self, value: f32) -> Result<()> {
495        self.write_document(&Value::Number(Number::from(value)))
496    }
497    fn serialize_f64(self, value: f64) -> Result<()> {
498        self.write_document(&Value::Number(Number::from(value)))
499    }
500    fn serialize_char(self, value: char) -> Result<()> {
501        self.write_document(&Value::String(value.to_string()))
502    }
503    fn serialize_str(self, value: &str) -> Result<()> {
504        self.write_document(&Value::String(value.to_owned()))
505    }
506    fn serialize_bytes(self, _value: &[u8]) -> Result<()> {
507        Err(Error::message(
508            "serialization and deserialization of bytes in YAML is not implemented",
509        ))
510    }
511    fn serialize_none(self) -> Result<()> {
512        self.write_document(&Value::Null)
513    }
514    fn serialize_some<T>(self, value: &T) -> Result<()>
515    where
516        T: ?Sized + Serialize,
517    {
518        self.collect(value)
519    }
520    fn serialize_unit(self) -> Result<()> {
521        self.write_document(&Value::Null)
522    }
523    fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
524        self.serialize_unit()
525    }
526    fn serialize_unit_variant(
527        self,
528        _name: &'static str,
529        _index: u32,
530        variant: &'static str,
531    ) -> Result<()> {
532        self.write_document(&Value::String(variant.to_owned()))
533    }
534    fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<()>
535    where
536        T: ?Sized + Serialize,
537    {
538        let value =
539            ser::Serializer::serialize_newtype_struct(crate::value::Serializer, name, value)?;
540        self.write_document(&value)
541    }
542    fn serialize_newtype_variant<T>(
543        self,
544        _name: &'static str,
545        _index: u32,
546        variant: &'static str,
547        value: &T,
548    ) -> Result<()>
549    where
550        T: ?Sized + Serialize,
551    {
552        let value = crate::to_value(value)?;
553        if matches!(value, Value::Tagged(..)) {
554            return Err(Error::message(
555                "serializing nested enums in YAML is not supported",
556            ));
557        }
558        self.write_document(&Value::Tagged(Box::new(TaggedValue {
559            tag: Tag::new(variant),
560            value,
561        })))
562    }
563    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
564        Ok(DocumentSequence::sequence(self, len, None))
565    }
566    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
567        Ok(DocumentSequence::sequence(self, Some(len), None))
568    }
569    fn serialize_tuple_struct(
570        self,
571        _name: &'static str,
572        len: usize,
573    ) -> Result<Self::SerializeTupleStruct> {
574        Ok(DocumentSequence::sequence(self, Some(len), None))
575    }
576    fn serialize_tuple_variant(
577        self,
578        _name: &'static str,
579        _index: u32,
580        variant: &'static str,
581        len: usize,
582    ) -> Result<Self::SerializeTupleVariant> {
583        Ok(DocumentSequence::sequence(
584            self,
585            Some(len),
586            Some(variant.to_owned()),
587        ))
588    }
589    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
590        Ok(DocumentSequence::mapping(self, len, None))
591    }
592    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
593        Ok(DocumentSequence::mapping(self, Some(len), None))
594    }
595    fn serialize_struct_variant(
596        self,
597        _name: &'static str,
598        _index: u32,
599        variant: &'static str,
600        len: usize,
601    ) -> Result<Self::SerializeStructVariant> {
602        Ok(DocumentSequence::mapping(
603            self,
604            Some(len),
605            Some(variant.to_owned()),
606        ))
607    }
608    fn collect_str<T>(self, value: &T) -> Result<()>
609    where
610        T: ?Sized + std::fmt::Display,
611    {
612        self.serialize_str(&value.to_string())
613    }
614    fn is_human_readable(&self) -> bool {
615        true
616    }
617}
618
619fn render_value(value: &Value, indent: usize, output: &mut String) {
620    enum RenderAction<'a> {
621        Value(&'a Value, usize),
622        Nested(&'a Value, usize),
623        Inline(&'a Value),
624        Indent(usize),
625        Text(&'static str),
626    }
627
628    let mut pending = vec![RenderAction::Value(value, indent)];
629    while let Some(action) = pending.pop() {
630        match action {
631            RenderAction::Text(text) => output.push_str(text),
632            RenderAction::Indent(indent) => push_indent(output, indent),
633            RenderAction::Inline(value) => render_inline(value, output),
634            RenderAction::Nested(value, indent) => match value {
635                Value::Tagged(tagged) if !is_inline(&tagged.value) => {
636                    output.push(' ');
637                    output.push('!');
638                    output.push_str(tagged.tag.as_suffix());
639                    output.push('\n');
640                    pending.push(RenderAction::Value(&tagged.value, indent));
641                }
642                _ if is_inline(value) => {
643                    output.push(' ');
644                    pending.push(RenderAction::Inline(value));
645                }
646                _ => {
647                    output.push('\n');
648                    pending.push(RenderAction::Value(value, indent));
649                }
650            },
651            RenderAction::Value(value, indent) => match value {
652                Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
653                    push_indent(output, indent);
654                    render_scalar(value, output);
655                }
656                Value::Sequence(values) if values.is_empty() => {
657                    push_indent(output, indent);
658                    output.push_str("[]");
659                }
660                Value::Sequence(values) => {
661                    for (index, value) in values.iter().enumerate().rev() {
662                        pending.push(RenderAction::Nested(value, indent + 2));
663                        pending.push(RenderAction::Text("-"));
664                        pending.push(RenderAction::Indent(indent));
665                        if index > 0 {
666                            pending.push(RenderAction::Text("\n"));
667                        }
668                    }
669                }
670                Value::Mapping(entries) if entries.is_empty() => {
671                    push_indent(output, indent);
672                    output.push_str("{}");
673                }
674                Value::Mapping(entries) => {
675                    for (index, (key, value)) in entries.iter().enumerate().rev() {
676                        pending.push(RenderAction::Nested(value, indent + 2));
677                        if is_inline(key) {
678                            pending.push(RenderAction::Text(":"));
679                            pending.push(RenderAction::Inline(key));
680                            pending.push(RenderAction::Indent(indent));
681                        } else {
682                            pending.push(RenderAction::Text(":"));
683                            pending.push(RenderAction::Indent(indent));
684                            pending.push(RenderAction::Text("\n"));
685                            pending.push(RenderAction::Nested(key, indent + 2));
686                            pending.push(RenderAction::Text("?"));
687                            pending.push(RenderAction::Indent(indent));
688                        }
689                        if index > 0 {
690                            pending.push(RenderAction::Text("\n"));
691                        }
692                    }
693                }
694                Value::Tagged(tagged) => {
695                    push_indent(output, indent);
696                    output.push('!');
697                    output.push_str(tagged.tag.as_suffix());
698                    if is_inline(&tagged.value) {
699                        output.push(' ');
700                        pending.push(RenderAction::Inline(&tagged.value));
701                    } else {
702                        output.push('\n');
703                        pending.push(RenderAction::Value(&tagged.value, indent));
704                    }
705                }
706            },
707        }
708    }
709}
710
711fn is_inline(value: &Value) -> bool {
712    let mut value = value;
713    while let Value::Tagged(tagged) = value {
714        value = &tagged.value;
715    }
716    matches!(
717        value,
718        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_)
719    ) || matches!(value, Value::Sequence(values) if values.is_empty())
720        || matches!(value, Value::Mapping(entries) if entries.is_empty())
721}
722
723fn render_inline(mut value: &Value, output: &mut String) {
724    while let Value::Tagged(tagged) = value {
725        output.push('!');
726        output.push_str(tagged.tag.as_suffix());
727        output.push(' ');
728        value = &tagged.value;
729    }
730    match value {
731        Value::Sequence(values) if values.is_empty() => output.push_str("[]"),
732        Value::Mapping(entries) if entries.is_empty() => output.push_str("{}"),
733        _ => render_scalar(value, output),
734    }
735}
736
737fn render_scalar(value: &Value, output: &mut String) {
738    match value {
739        Value::Null => output.push_str("null"),
740        Value::Bool(value) => output.push_str(if *value { "true" } else { "false" }),
741        Value::Number(value) => output.push_str(&value.to_string()),
742        Value::String(value) => render_string(value, output),
743        _ => unreachable!("collections are not scalars"),
744    }
745}
746
747fn render_string(value: &str, output: &mut String) {
748    if is_safe_plain(value) {
749        output.push_str(value);
750        return;
751    }
752    output.push('"');
753    for character in value.chars() {
754        match character {
755            '"' => output.push_str("\\\""),
756            '\\' => output.push_str("\\\\"),
757            '\n' => output.push_str("\\n"),
758            '\r' => output.push_str("\\r"),
759            '\t' => output.push_str("\\t"),
760            '\u{08}' => output.push_str("\\b"),
761            '\u{0C}' => output.push_str("\\f"),
762            character if character.is_control() => {
763                use std::fmt::Write as _;
764                let _ = write!(output, "\\u{:04X}", character as u32);
765            }
766            character => output.push(character),
767        }
768    }
769    output.push('"');
770}
771
772fn is_safe_plain(value: &str) -> bool {
773    if value.is_empty() || value.trim() != value || value.contains(['\n', '\r', '\t']) {
774        return false;
775    }
776    if value.starts_with([
777        '-', '?', ':', ',', '[', ']', '{', '}', '#', '&', '*', '!', '|', '>', '\'', '"', '%', '@',
778        '`',
779    ]) {
780        return false;
781    }
782    if value.contains(": ") || value.contains(" #") || value == "---" || value == "..." {
783        return false;
784    }
785    if matches!(
786        value,
787        "~" | "null"
788            | "Null"
789            | "NULL"
790            | "true"
791            | "True"
792            | "TRUE"
793            | "false"
794            | "False"
795            | "FALSE"
796            | ".inf"
797            | ".Inf"
798            | ".INF"
799            | "-.inf"
800            | "-.Inf"
801            | "-.INF"
802            | ".nan"
803            | ".NaN"
804            | ".NAN"
805    ) {
806        return false;
807    }
808    !looks_numeric(value)
809}
810
811fn looks_numeric(value: &str) -> bool {
812    let value = value.replace('_', "");
813    let unsigned = value.strip_prefix(['+', '-']).unwrap_or(&value);
814    if unsigned
815        .strip_prefix("0x")
816        .is_some_and(|v| !v.is_empty() && v.chars().all(|c| c.is_ascii_hexdigit()))
817    {
818        return true;
819    }
820    if unsigned
821        .strip_prefix("0o")
822        .is_some_and(|v| !v.is_empty() && v.chars().all(|c| matches!(c, '0'..='7')))
823    {
824        return true;
825    }
826    if unsigned
827        .strip_prefix("0b")
828        .is_some_and(|v| !v.is_empty() && v.chars().all(|c| matches!(c, '0' | '1')))
829    {
830        return true;
831    }
832    value.parse::<i128>().is_ok() || value.parse::<u128>().is_ok() || value.parse::<f64>().is_ok()
833}
834
835fn push_indent(output: &mut String, indent: usize) {
836    output.extend(std::iter::repeat_n(' ', indent));
837}
838
839#[cfg(test)]
840mod tests {
841    use super::*;
842
843    #[test]
844    fn renderer_handles_deep_sequences_iteratively() {
845        let depth = 1024;
846        let mut value = Value::String("value".to_owned());
847        for _ in 0..depth {
848            value = Value::Sequence(vec![value]);
849        }
850
851        let mut output = String::new();
852        render_value(&value, 0, &mut output);
853        assert_eq!(output.matches('-').count(), depth);
854        assert!(output.ends_with(&format!("{}- value", "  ".repeat(depth - 1))));
855    }
856
857    #[test]
858    fn renderer_handles_tag_chains_iteratively() {
859        let mut value = Value::String("value".to_owned());
860        for index in (0..1024).rev() {
861            value = Value::Tagged(Box::new(TaggedValue {
862                tag: Tag::new(format!("tag{index}")),
863                value,
864            }));
865        }
866
867        let mut output = String::new();
868        render_value(&value, 0, &mut output);
869        assert!(output.starts_with("!tag0 !tag1 !tag2 "));
870        assert!(output.ends_with("!tag1023 value"));
871    }
872}