Skip to main content

tanzim_value/
value.rs

1use std::fmt::{Debug, Display, Formatter};
2use std::num::NonZeroU32;
3use tanzim_source::Source;
4
5/// Source and optional position of a configuration value.
6///
7/// Holds the full originating [`Source`] (name, options, resource — including any `on_error`
8/// policy), so a value or error can be traced back to where and how it was declared. Positions are
9/// 1-based and stored as [`NonZeroU32`]; [`crate::Error`] boxes the [`Location`] so results stay
10/// small. Construct via [`Location::in_source`] (the real source) or [`Location::at`] (a bare
11/// name/resource, for synthetic origins).
12#[derive(Debug, Clone, PartialEq)]
13pub struct Location {
14    /// The originating [`Source`] (name, options, resource, `on_error` policy).
15    pub source: Source,
16    /// 1-based line number, when known.
17    pub line: Option<NonZeroU32>,
18    /// 1-based column number, when known.
19    pub column: Option<NonZeroU32>,
20    /// UTF-8 character span length for error underlines; defaults to one caret.
21    pub length: Option<NonZeroU32>,
22    /// Pre-rendered `{error:#}` snippet: the ±3-line source window with gutter line numbers and a
23    /// `^^^` caret line under the offending span, already formatted at construction. Empty for
24    /// synthetic locations (no source text) or when there is no line to point at. Display just
25    /// prints this — it never re-computes it. Build it with [`Location::in_text`].
26    pub snippet: String,
27}
28
29/// Convert a 1-based `usize` position into the compact [`NonZeroU32`] storage.
30///
31/// Returns `None` for zero (treated as "no position") and clamps values larger
32/// than [`u32::MAX`] to `u32::MAX` rather than overflowing.
33fn position(value: usize) -> Option<NonZeroU32> {
34    NonZeroU32::new(u32::try_from(value).unwrap_or(u32::MAX))
35}
36
37impl Location {
38    /// Build a location from the full originating [`Source`], with no source snippet.
39    ///
40    /// Use [`Location::in_text`] instead when the source text is on hand, so `{error:#}` can render
41    /// a caret window; this constructor leaves [`snippet`](Self::snippet) empty.
42    pub fn in_source(
43        source: Source,
44        line: Option<usize>,
45        column: Option<usize>,
46        length: Option<usize>,
47    ) -> Self {
48        Self {
49            source,
50            line: line.and_then(position),
51            column: column.and_then(position),
52            length: length.and_then(position),
53            snippet: String::new(),
54        }
55    }
56
57    /// Build a location from the originating [`Source`] and its raw `text`, pre-rendering the
58    /// `{error:#}` [`snippet`](Self::snippet): the offending line plus three lines of context on
59    /// either side, each with a gutter line number, and a `^` caret line under the `column`/`length`
60    /// span. When `line` is `None` (e.g. single-line input with no position) the snippet is left
61    /// empty. The window is clamped to the file's bounds.
62    pub fn in_text(
63        source: Source,
64        text: &str,
65        line: Option<usize>,
66        column: Option<usize>,
67        length: Option<usize>,
68    ) -> Self {
69        let mut snippet = String::new();
70        if let Some(line_number) = line {
71            let highlight = length.unwrap_or(1).max(1);
72            let lines: Vec<&str> = text.split('\n').collect();
73            let offending = line_number.saturating_sub(1);
74            let start = offending.saturating_sub(3);
75            let end = (offending + 4).min(lines.len());
76            let gutter_width = end.to_string().len();
77            let mut rows: Vec<String> = Vec::new();
78            for (offset, line_text) in lines[start..end].iter().enumerate() {
79                let display_line = start + offset + 1;
80                let number = display_line.to_string();
81                let pad = gutter_width.saturating_sub(number.len());
82                let mut row = String::from("  ");
83                for _ in 0..pad {
84                    row.push(' ');
85                }
86                row.push_str(&number);
87                row.push_str(" | ");
88                row.push_str(line_text);
89                rows.push(row);
90                if display_line == line_number {
91                    let mut caret = String::from("  ");
92                    for _ in 0..pad + number.len() + 1 {
93                        caret.push(' ');
94                    }
95                    caret.push_str("| ");
96                    if let Some(column_number) = column {
97                        for _ in 1..column_number {
98                            caret.push(' ');
99                        }
100                    }
101                    for _ in 0..highlight {
102                        caret.push('^');
103                    }
104                    rows.push(caret);
105                }
106            }
107            snippet = rows.join("\n");
108        }
109        Self {
110            source,
111            line: line.and_then(position),
112            column: column.and_then(position),
113            length: length.and_then(position),
114            snippet,
115        }
116    }
117
118    /// Build a location from a bare source name and resource (a synthetic [`Source`] with no
119    /// options), for origins that do not come from parsing a real source string. No snippet.
120    pub fn at(
121        source_name: &str,
122        resource: &str,
123        line: Option<usize>,
124        column: Option<usize>,
125        length: Option<usize>,
126    ) -> Self {
127        Self::in_source(
128            Source::named(source_name).with_resource(resource),
129            line,
130            column,
131            length,
132        )
133    }
134
135    /// The originating source's name (loader kind).
136    pub fn source_name(&self) -> &str {
137        self.source.source()
138    }
139
140    /// The originating source's resource (address).
141    pub fn resource(&self) -> &str {
142        self.source.resource()
143    }
144
145    /// Builder: set the UTF-8 character span length used for error underlines.
146    pub fn with_length(mut self, length: usize) -> Self {
147        self.length = position(length);
148        self
149    }
150}
151
152impl Display for Location {
153    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154        let resource = self.source.resource();
155        if resource.is_empty() {
156            write!(f, "{}", self.source.source())?;
157        } else {
158            write!(f, "{}:{}", self.source.source(), resource)?;
159        }
160        match (self.line, self.column) {
161            (Some(line), Some(column)) => write!(f, ":{line}:{column}"),
162            (Some(line), None) => write!(f, ":{line}"),
163            _ => Ok(()),
164        }
165    }
166}
167
168/// Kind of value stored in [`Value`].
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
170pub enum ValueType {
171    /// A boolean value.
172    Bool,
173    /// A signed integer value.
174    Int,
175    /// A floating-point value.
176    Float,
177    /// A string value.
178    String,
179    /// A list of values.
180    List,
181    /// A map of keys to values.
182    Map,
183    /// The absence of a value.
184    Null,
185}
186
187impl Display for ValueType {
188    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
189        f.write_str(match self {
190            Self::Bool => "boolean",
191            Self::Int => "integer",
192            Self::Float => "float",
193            Self::String => "string",
194            Self::List => "list",
195            Self::Map => "map",
196            Self::Null => "null",
197        })
198    }
199}
200
201/// Ordered map of configuration keys to located values (last key wins on lookup).
202///
203/// Backed by a `Vec` of `(key, value)` pairs rather than a hash map, so insertion order is
204/// preserved for display and iteration. Lookups scan from the end, so a repeated key shadows
205/// its earlier insertion without removing it.
206///
207/// ```rust
208/// use tanzim_value::{Location, Map, Value, LocatedValue};
209///
210/// let mut map = Map::new();
211/// let location = Location::at("env", "", None, None, None);
212/// map.insert(
213///     "port".to_string(),
214///     LocatedValue::new(Value::Int(8080), location.clone()),
215/// );
216/// map.insert(
217///     "host".to_string(),
218///     LocatedValue::new(Value::String("localhost".to_string()), location),
219/// );
220///
221/// assert_eq!(map.len(), 2);
222/// assert!(map.contains_key("port"));
223/// assert_eq!(map.get("port").unwrap().value().as_int(), Some(8080));
224/// ```
225#[derive(Debug, Clone, PartialEq, Default)]
226pub struct Map {
227    entries: Vec<(String, LocatedValue)>,
228}
229
230impl Map {
231    /// Create an empty map.
232    pub fn new() -> Self {
233        Self::default()
234    }
235
236    /// Number of entries, including any shadowed (repeated-key) entries.
237    pub fn len(&self) -> usize {
238        self.entries.len()
239    }
240
241    /// `true` when the map has no entries.
242    pub fn is_empty(&self) -> bool {
243        self.entries.is_empty()
244    }
245
246    /// `true` when `key` is present (checking the most recently inserted occurrence).
247    pub fn contains_key(&self, key: &str) -> bool {
248        for index in (0..self.entries.len()).rev() {
249            if self.entries[index].0 == key {
250                return true;
251            }
252        }
253        false
254    }
255
256    /// The value for `key`, or `None` if absent. When `key` was inserted more than once, this
257    /// returns the most recently inserted value.
258    pub fn get(&self, key: &str) -> Option<&LocatedValue> {
259        for index in (0..self.entries.len()).rev() {
260            if self.entries[index].0 == key {
261                return Some(&self.entries[index].1);
262            }
263        }
264        None
265    }
266
267    /// Mutable access to the value for `key`, or `None` if absent.
268    pub fn get_mut(&mut self, key: &str) -> Option<&mut LocatedValue> {
269        let mut found = None;
270        for index in (0..self.entries.len()).rev() {
271            if self.entries[index].0 == key {
272                found = Some(index);
273                break;
274            }
275        }
276        if let Some(index) = found {
277            Some(&mut self.entries[index].1)
278        } else {
279            None
280        }
281    }
282
283    /// Insert `value` under `key`, removing (and returning) any prior value for that key.
284    pub fn insert(&mut self, key: String, value: LocatedValue) -> Option<LocatedValue> {
285        let old = self.remove(&key);
286        self.entries.push((key, value));
287        old
288    }
289
290    /// Remove and return the value for `key`, if present.
291    pub fn remove(&mut self, key: &str) -> Option<LocatedValue> {
292        let mut found = None;
293        for index in (0..self.entries.len()).rev() {
294            if self.entries[index].0 == key {
295                found = Some(index);
296                break;
297            }
298        }
299        if let Some(index) = found {
300            Some(self.entries.remove(index).1)
301        } else {
302            None
303        }
304    }
305
306    /// All entries in insertion order, including any shadowed (repeated-key) entries.
307    pub fn entries(&self) -> &[(String, LocatedValue)] {
308        &self.entries
309    }
310
311    /// Mutable access to all entries, for in-place edits, reordering, or removal.
312    pub fn entries_mut(&mut self) -> &mut Vec<(String, LocatedValue)> {
313        &mut self.entries
314    }
315}
316
317impl Display for Map {
318    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
319        let alternate = f.alternate();
320        let mut map = f.debug_map();
321        for (key, value) in &self.entries {
322            if alternate {
323                map.entry(key, &format_args!("{:#}", value));
324            } else {
325                map.entry(key, &format_args!("{}", value));
326            }
327        }
328        map.finish()
329    }
330}
331
332/// Dynamically typed configuration value.
333///
334/// The tree shape produced by parsing configuration input: booleans, integers, floats, strings,
335/// lists, maps, and null. Use the `is_*`/`as_*`/`into_*` family of methods to inspect or extract
336/// a specific variant without a `match`.
337///
338/// ```rust
339/// use tanzim_value::Value;
340///
341/// let value: Value = 8080isize.into();
342/// assert!(value.is_int());
343/// assert_eq!(value.as_int(), Some(8080));
344///
345/// let value: Value = "localhost".into();
346/// assert_eq!(value.as_string().map(String::as_str), Some("localhost"));
347/// ```
348#[derive(Debug, Clone, PartialEq)]
349pub enum Value {
350    /// A boolean value.
351    Bool(bool),
352    /// A signed integer value.
353    Int(isize),
354    /// A floating-point value.
355    Float(f64),
356    /// A string value.
357    String(String),
358    /// A list of located values.
359    List(Vec<LocatedValue>),
360    /// A map of keys to located values.
361    Map(Map),
362    /// The absence of a value.
363    Null,
364}
365
366impl From<bool> for Value {
367    fn from(value: bool) -> Self {
368        Value::Bool(value)
369    }
370}
371
372impl From<isize> for Value {
373    fn from(value: isize) -> Self {
374        Value::Int(value)
375    }
376}
377
378impl From<f64> for Value {
379    fn from(value: f64) -> Self {
380        Value::Float(value)
381    }
382}
383
384impl From<String> for Value {
385    fn from(value: String) -> Self {
386        Value::String(value)
387    }
388}
389
390impl From<&str> for Value {
391    fn from(value: &str) -> Self {
392        Value::String(value.to_string())
393    }
394}
395
396/// Comment text attached to a [`LocatedValue`]: lines preceding the key and an optional
397/// inline comment on the same line as the value.
398///
399/// Empty by default — callers check `.before().is_empty()` and `.after()` to detect absence.
400#[derive(Debug, Clone, PartialEq, Default)]
401pub struct Comment {
402    before: Vec<String>,
403    after: Option<String>,
404}
405
406impl Comment {
407    /// Create an empty comment (no before-lines, no inline comment).
408    pub fn new() -> Self {
409        Self::default()
410    }
411
412    /// Comment lines preceding the key; empty when none.
413    pub fn before(&self) -> &[String] {
414        &self.before
415    }
416
417    /// Mutable access to the before-lines.
418    pub fn before_mut(&mut self) -> &mut Vec<String> {
419        &mut self.before
420    }
421
422    /// Inline comment on the same line as the value; `None` when absent.
423    pub fn after(&self) -> Option<&str> {
424        self.after.as_deref()
425    }
426
427    /// Mutable access to the inline after-comment.
428    pub fn after_mut(&mut self) -> &mut Option<String> {
429        &mut self.after
430    }
431
432    /// Builder: set the before-lines (replaces any existing).
433    pub fn with_before(mut self, lines: impl IntoIterator<Item = impl Into<String>>) -> Self {
434        self.before = lines.into_iter().map(|l| l.into()).collect();
435        self
436    }
437
438    /// Builder: set the inline after-comment.
439    pub fn with_after(mut self, text: Option<impl Into<String>>) -> Self {
440        self.after = text.map(|t| t.into());
441        self
442    }
443
444    /// In-place setter for before-lines.
445    pub fn set_before(&mut self, lines: impl IntoIterator<Item = impl Into<String>>) {
446        self.before = lines.into_iter().map(|l| l.into()).collect();
447    }
448
449    /// In-place setter for the inline after-comment.
450    pub fn set_after(&mut self, text: Option<impl Into<String>>) {
451        self.after = text.map(|t| t.into());
452    }
453}
454
455/// A [`Value`] with its [`Location`] and an optional [`Comment`].
456///
457/// Fields are private; build with [`LocatedValue::new`] and access through the provided
458/// accessor methods. [`Display`] is compact by default; use `{value:#}` for a multiline dump
459/// with `@source:resource:line:column` on the first line.
460///
461/// ```rust
462/// use tanzim_value::{LocatedValue, Location, Value};
463///
464/// let location = Location::at("env", "PORT", None, None, None);
465/// let located = LocatedValue::new(Value::Int(8080), location);
466///
467/// assert_eq!(located.value().as_int(), Some(8080));
468/// assert_eq!(located.location().source_name(), "env");
469/// assert_eq!(format!("{located}"), "8080");
470/// ```
471#[derive(Debug, Clone, PartialEq)]
472pub struct LocatedValue {
473    value: Value,
474    location: Location,
475    comment: Comment,
476}
477
478impl LocatedValue {
479    /// Create a located value with an empty (default) comment.
480    pub fn new(value: impl Into<Value>, location: impl Into<Location>) -> Self {
481        Self {
482            value: value.into(),
483            location: location.into(),
484            comment: Comment::new(),
485        }
486    }
487
488    // --- value ---
489
490    /// The wrapped [`Value`].
491    pub fn value(&self) -> &Value {
492        &self.value
493    }
494
495    /// Mutable access to the wrapped [`Value`].
496    pub fn value_mut(&mut self) -> &mut Value {
497        &mut self.value
498    }
499
500    /// Consume this located value, discarding location and comment, and return the [`Value`].
501    pub fn into_value(self) -> Value {
502        self.value
503    }
504
505    /// Builder: replace the wrapped value.
506    pub fn with_value(mut self, value: impl Into<Value>) -> Self {
507        self.value = value.into();
508        self
509    }
510
511    /// In-place setter for the wrapped value.
512    pub fn set_value(&mut self, value: impl Into<Value>) {
513        self.value = value.into();
514    }
515
516    // --- location ---
517
518    /// The value's [`Location`].
519    pub fn location(&self) -> &Location {
520        &self.location
521    }
522
523    /// Mutable access to the [`Location`].
524    pub fn location_mut(&mut self) -> &mut Location {
525        &mut self.location
526    }
527
528    /// Builder: replace the location.
529    pub fn with_location(mut self, location: impl Into<Location>) -> Self {
530        self.location = location.into();
531        self
532    }
533
534    /// In-place setter for the location.
535    pub fn set_location(&mut self, location: impl Into<Location>) {
536        self.location = location.into();
537    }
538
539    // --- comment ---
540
541    /// The value's [`Comment`] (empty when none was attached).
542    pub fn comment(&self) -> &Comment {
543        &self.comment
544    }
545
546    /// Mutable access to the [`Comment`].
547    pub fn comment_mut(&mut self) -> &mut Comment {
548        &mut self.comment
549    }
550
551    /// Builder: replace the comment.
552    pub fn with_comment(mut self, comment: Comment) -> Self {
553        self.comment = comment;
554        self
555    }
556
557    /// In-place setter for the comment.
558    pub fn set_comment(&mut self, comment: Comment) {
559        self.comment = comment;
560    }
561}
562
563impl Display for LocatedValue {
564    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
565        if f.alternate() {
566            let mut map = f.debug_map();
567            map.entry(&"value", &format_args!("{:#}", self.value));
568            map.entry(
569                &"location",
570                &format_args!("{:?}", self.location.to_string()),
571            );
572            if !self.comment.before.is_empty() || self.comment.after.is_some() {
573                map.entry(&"comment_before", &self.comment.before.as_slice());
574                if let Some(after) = &self.comment.after {
575                    map.entry(&"comment_after", &after.as_str());
576                }
577            }
578            map.finish()
579        } else {
580            write!(f, "{}", self.value)
581        }
582    }
583}
584
585impl AsRef<Value> for Value {
586    fn as_ref(&self) -> &Value {
587        self
588    }
589}
590
591impl AsRef<Value> for LocatedValue {
592    fn as_ref(&self) -> &Value {
593        &self.value
594    }
595}
596
597impl Value {
598    /// Create an empty [`Value::Map`].
599    pub fn new_map() -> Self {
600        Self::Map(Map::new())
601    }
602
603    /// Create an empty [`Value::List`].
604    pub fn new_list() -> Self {
605        Self::List(Vec::new())
606    }
607
608    /// Create an empty [`Value::String`].
609    pub fn new_string() -> Self {
610        Self::String(String::new())
611    }
612
613    /// `true` if this is a [`Value::Bool`].
614    pub fn is_bool(&self) -> bool {
615        matches!(self, Self::Bool(_))
616    }
617
618    /// The boolean, if this is a [`Value::Bool`].
619    pub fn as_bool(&self) -> Option<bool> {
620        match self {
621            Self::Bool(value) => Some(*value),
622            _ => None,
623        }
624    }
625
626    /// Consume and return the boolean, if this is a [`Value::Bool`].
627    pub fn into_bool(self) -> Option<bool> {
628        match self {
629            Self::Bool(value) => Some(value),
630            _ => None,
631        }
632    }
633
634    /// Mutable access to the boolean, if this is a [`Value::Bool`].
635    pub fn bool_mut(&mut self) -> Option<&mut bool> {
636        match self {
637            Self::Bool(value) => Some(value),
638            _ => None,
639        }
640    }
641
642    /// `true` if this is a [`Value::Int`].
643    pub fn is_int(&self) -> bool {
644        matches!(self, Self::Int(_))
645    }
646
647    /// The integer, if this is a [`Value::Int`].
648    pub fn as_int(&self) -> Option<isize> {
649        match self {
650            Self::Int(value) => Some(*value),
651            _ => None,
652        }
653    }
654
655    /// Consume and return the integer, if this is a [`Value::Int`].
656    pub fn into_int(self) -> Option<isize> {
657        match self {
658            Self::Int(value) => Some(value),
659            _ => None,
660        }
661    }
662
663    /// Mutable access to the integer, if this is a [`Value::Int`].
664    pub fn int_mut(&mut self) -> Option<&mut isize> {
665        match self {
666            Self::Int(value) => Some(value),
667            _ => None,
668        }
669    }
670
671    /// `true` if this is a [`Value::Float`].
672    pub fn is_float(&self) -> bool {
673        matches!(self, Self::Float(_))
674    }
675
676    /// The float, if this is a [`Value::Float`].
677    pub fn as_float(&self) -> Option<f64> {
678        match self {
679            Self::Float(value) => Some(*value),
680            _ => None,
681        }
682    }
683
684    /// Consume and return the float, if this is a [`Value::Float`].
685    pub fn into_float(self) -> Option<f64> {
686        match self {
687            Self::Float(value) => Some(value),
688            _ => None,
689        }
690    }
691
692    /// Mutable access to the float, if this is a [`Value::Float`].
693    pub fn float_mut(&mut self) -> Option<&mut f64> {
694        match self {
695            Self::Float(value) => Some(value),
696            _ => None,
697        }
698    }
699
700    /// `true` if this is a [`Value::String`].
701    pub fn is_string(&self) -> bool {
702        matches!(self, Self::String(_))
703    }
704
705    /// The string, if this is a [`Value::String`].
706    pub fn as_string(&self) -> Option<&String> {
707        match self {
708            Self::String(value) => Some(value),
709            _ => None,
710        }
711    }
712
713    /// Consume and return the string, if this is a [`Value::String`].
714    pub fn into_string(self) -> Option<String> {
715        match self {
716            Self::String(value) => Some(value),
717            _ => None,
718        }
719    }
720
721    /// Mutable access to the string, if this is a [`Value::String`].
722    pub fn string_mut(&mut self) -> Option<&mut String> {
723        match self {
724            Self::String(value) => Some(value),
725            _ => None,
726        }
727    }
728
729    /// `true` if this is a [`Value::List`].
730    pub fn is_list(&self) -> bool {
731        matches!(self, Self::List(_))
732    }
733
734    /// The list, if this is a [`Value::List`].
735    pub fn as_list(&self) -> Option<&Vec<LocatedValue>> {
736        match self {
737            Self::List(value) => Some(value),
738            _ => None,
739        }
740    }
741
742    /// Consume and return the list, if this is a [`Value::List`].
743    pub fn into_list(self) -> Option<Vec<LocatedValue>> {
744        match self {
745            Self::List(value) => Some(value),
746            _ => None,
747        }
748    }
749
750    /// Mutable access to the list, if this is a [`Value::List`].
751    pub fn list_mut(&mut self) -> Option<&mut Vec<LocatedValue>> {
752        match self {
753            Self::List(value) => Some(value),
754            _ => None,
755        }
756    }
757
758    /// `true` if this is a [`Value::Map`].
759    pub fn is_map(&self) -> bool {
760        matches!(self, Self::Map(_))
761    }
762
763    /// The map, if this is a [`Value::Map`].
764    pub fn as_map(&self) -> Option<&Map> {
765        match self {
766            Self::Map(value) => Some(value),
767            _ => None,
768        }
769    }
770
771    /// Consume and return the map, if this is a [`Value::Map`].
772    pub fn into_map(self) -> Option<Map> {
773        match self {
774            Self::Map(value) => Some(value),
775            _ => None,
776        }
777    }
778
779    /// Mutable access to the map, if this is a [`Value::Map`].
780    pub fn map_mut(&mut self) -> Option<&mut Map> {
781        match self {
782            Self::Map(value) => Some(value),
783            _ => None,
784        }
785    }
786
787    /// `true` if this is a [`Value::Null`].
788    pub fn is_null(&self) -> bool {
789        matches!(self, Self::Null)
790    }
791
792    /// This value's [`ValueType`].
793    pub fn type_name(&self) -> ValueType {
794        match self {
795            Self::Bool(_) => ValueType::Bool,
796            Self::Int(_) => ValueType::Int,
797            Self::Float(_) => ValueType::Float,
798            Self::String(_) => ValueType::String,
799            Self::List(_) => ValueType::List,
800            Self::Map(_) => ValueType::Map,
801            Self::Null => ValueType::Null,
802        }
803    }
804}
805
806impl Display for Value {
807    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
808        match self {
809            Self::Bool(value) => write!(f, "{value}"),
810            Self::Int(value) => write!(f, "{value}"),
811            Self::Float(value) => write!(f, "{value}"),
812            Self::String(value) => write!(f, "{value:?}"),
813            Self::List(values) => {
814                let alternate = f.alternate();
815                let mut list = f.debug_list();
816                for value in values {
817                    if alternate {
818                        list.entry(&format_args!("{:#}", value));
819                    } else {
820                        list.entry(&format_args!("{}", value));
821                    }
822                }
823                list.finish()
824            }
825            Self::Map(value) => Display::fmt(value, f),
826            Self::Null => f.write_str("null"),
827        }
828    }
829}