Skip to main content

ephemeris_documents/json/
cst.rs

1//! Concrete syntax tree of a JSON5 document.
2//!
3//! Every byte of the source is owned by exactly one node, so printing the tree
4//! returns the source unchanged. The document stores its [`Dialect`] because
5//! the comma rule and the spelling of new keys depend on it, and that keeps
6//! `to_string` the one way to emit.
7
8mod array;
9mod key;
10mod object;
11mod quote;
12mod scalar;
13mod tail;
14mod trivia;
15
16use std::fmt;
17
18pub(super) use self::array::{Array, Item};
19pub(super) use self::key::Key;
20pub(super) use self::object::{Member, Object};
21pub(super) use self::scalar::Scalar;
22pub(super) use self::tail::{Comma, Position, Tail};
23use self::trivia::Indent;
24pub(super) use self::trivia::Trivia;
25use super::Dialect;
26use super::parser::Parser;
27use crate::annotation::{Annotated, AnnotatedValue};
28use crate::backend::Editable;
29use crate::error::JsonError;
30use crate::header::Applied;
31
32/// The root of a parsed or built document.
33#[derive(Debug, Clone)]
34pub(crate) struct Document {
35    dialect: Dialect,
36    pub(super) value: Value,
37    trailing: Trivia,
38    /// One level of indentation, for members inserted into the document.
39    indent: Indent,
40}
41
42impl Document {
43    /// A parsed document. Its indent is the unit of its first multi-line
44    /// member, or two spaces.
45    pub(super) fn parsed(dialect: Dialect, value: Value, trailing: Trivia) -> Self {
46        let indent = value
47            .as_object()
48            .and_then(Object::indent_unit)
49            .unwrap_or_default();
50        Self {
51            dialect,
52            value,
53            trailing,
54            indent,
55        }
56    }
57
58    /// End the document with exactly one line break.
59    pub(super) fn end_with_newline(&mut self) {
60        let text = [self.trailing.as_str().trim_end_matches('\n'), "\n"].concat();
61        self.trailing = Trivia::new(text);
62    }
63
64    /// Parse source as JSON5, whatever the dialect. The dialect only governs
65    /// how the document is written back.
66    ///
67    /// # Errors
68    /// Returns the first syntax error, with its byte offset.
69    pub(super) fn parse(source: &str, dialect: Dialect) -> Result<Self, JsonError> {
70        Parser::parse(source, dialect)
71    }
72
73    /// The canonical rendering of `tree`.
74    pub(super) fn build(tree: &Annotated, dialect: Dialect) -> Self {
75        let leading = Trivia::root(tree.comment.as_ref(), dialect);
76        Self {
77            dialect,
78            value: Value::build(tree, dialect, 0, leading),
79            trailing: Trivia::new("\n".to_owned()),
80            indent: Indent::default(),
81        }
82    }
83}
84
85impl Editable for Document {
86    type Node = Value;
87
88    fn header_text(&self) -> &str {
89        self.value.leading.as_str()
90    }
91
92    fn leading(&self) -> (&str, &str) {
93        ("", self.value.leading.as_str())
94    }
95
96    fn set_leading(&mut self, applied: Applied<'_>) {
97        self.value.leading = Trivia::new(applied.into_text());
98    }
99
100    fn root(&self) -> &Value {
101        &self.value
102    }
103
104    /// The root value, ready for members to be inserted.
105    fn root_mut(&mut self) -> &mut Value {
106        if let Kind::Object(object) = &mut self.value.kind {
107            object.set_unit(&self.indent);
108        }
109        &mut self.value
110    }
111}
112
113impl fmt::Display for Document {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        self.value.write(f, self.dialect)?;
116        write!(f, "{}", self.trailing)
117    }
118}
119
120/// A value with the trivia that precedes it.
121///
122/// For an object member or an array item the leading trivia is everything
123/// since the previous element, so a comment above a member moves with its
124/// value. In an object member it comes before the key.
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub(crate) struct Value {
127    pub(super) leading: Trivia,
128    pub(super) kind: Kind,
129}
130
131/// A container or a literal token.
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub(crate) enum Kind {
134    Object(Object),
135    Array(Array),
136    Scalar(Scalar),
137}
138
139impl Value {
140    pub(super) const fn new(leading: Trivia, kind: Kind) -> Self {
141        Self { leading, kind }
142    }
143
144    pub(super) const fn is_object(&self) -> bool {
145        matches!(self.kind, Kind::Object(_))
146    }
147
148    pub(super) const fn as_object(&self) -> Option<&Object> {
149        match &self.kind {
150            Kind::Object(object) => Some(object),
151            Kind::Array(_) | Kind::Scalar(_) => None,
152        }
153    }
154
155    pub(super) const fn as_object_mut(&mut self) -> Option<&mut Object> {
156        match &mut self.kind {
157            Kind::Object(object) => Some(object),
158            Kind::Array(_) | Kind::Scalar(_) => None,
159        }
160    }
161
162    fn build(node: &Annotated, dialect: Dialect, depth: usize, leading: Trivia) -> Self {
163        let kind = match &node.value {
164            AnnotatedValue::Scalar(leaf) => Kind::Scalar(Scalar::build(leaf, dialect)),
165            AnnotatedValue::Array(items) => Kind::Array(Array::build(items, dialect, depth)),
166            AnnotatedValue::Object(entries) => Kind::Object(Object::build(entries, dialect, depth)),
167        };
168        Self { leading, kind }
169    }
170
171    fn write(&self, f: &mut fmt::Formatter<'_>, dialect: Dialect) -> fmt::Result {
172        write!(f, "{}", self.leading)?;
173        self.kind.write(f, dialect)
174    }
175
176    /// Visit every piece of trivia in the value, including its leading trivia.
177    fn trivia_mut(&mut self, visit: &mut dyn FnMut(&mut Trivia)) {
178        visit(&mut self.leading);
179        self.kind.trivia_mut(visit);
180    }
181
182    /// Convert the indent of every line from the canonical two spaces per
183    /// level to `unit`, including the leading trivia.
184    fn reindent_unit(&mut self, unit: &Indent) {
185        self.trivia_mut(&mut |trivia| trivia.reindent_unit(unit));
186    }
187}
188
189impl Kind {
190    fn write(&self, f: &mut fmt::Formatter<'_>, dialect: Dialect) -> fmt::Result {
191        match self {
192            Self::Object(object) => object.write(f, dialect),
193            Self::Array(array) => array.write(f, dialect),
194            Self::Scalar(scalar) => f.write_str(scalar.text()),
195        }
196    }
197
198    fn trivia_mut(&mut self, visit: &mut dyn FnMut(&mut Trivia)) {
199        match self {
200            Self::Object(object) => object.trivia_mut(visit),
201            Self::Array(array) => array.trivia_mut(visit),
202            Self::Scalar(_) => {}
203        }
204    }
205
206    /// Replace the indent `from` at the start of every line with `to`.
207    fn reindent_from(&mut self, from: &str, to: &str) {
208        self.trivia_mut(&mut |trivia| trivia.reindent_from(from, to));
209    }
210}