1use std::io::Write;
2
3use serde::{Serialize, ser};
4
5use crate::value::{Mapping, Number, Tag, TaggedValue, Value};
6use crate::{Error, Result};
7
8pub 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
22pub 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
36pub struct Serializer<W> {
38 writer: W,
39 documents: usize,
40}
41
42impl<W> Serializer<W>
43where
44 W: Write,
45{
46 pub const fn new(writer: W) -> Self {
48 Self {
49 writer,
50 documents: 0,
51 }
52 }
53
54 pub fn flush(&mut self) -> Result<()> {
60 self.writer.flush().map_err(Error::io)
61 }
62
63 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}