Skip to main content

ephemeris_documents/json/
backend.rs

1//! The JSON and JSON5 backend of the generic merge.
2
3use super::Dialect;
4use super::cst::{Document, Kind, Value};
5use super::lexer::{Lexer, TokenKind};
6use crate::annotation::Annotated;
7use crate::backend::{Backend, Editable as _};
8use crate::error::{DocumentError, JsonError, JsonErrorKind};
9use crate::format::Format;
10use crate::header::Existing;
11
12/// Reconciles JSON or JSON5 documents through the syntax tree.
13#[derive(Debug, Clone, Copy)]
14pub(crate) struct Json {
15    dialect: Dialect,
16}
17
18impl Json {
19    pub(crate) const fn new(dialect: Dialect) -> Self {
20        Self { dialect }
21    }
22}
23
24impl Backend for Json {
25    type Document = Document;
26
27    fn parse(&self, source: &str) -> Result<Document, DocumentError> {
28        Ok(Document::parse(source, self.dialect)?)
29    }
30
31    fn build(&self, desired: &Annotated) -> Result<Document, DocumentError> {
32        Ok(Document::build(desired, self.dialect))
33    }
34
35    fn format(&self) -> Format {
36        self.dialect.format()
37    }
38
39    fn string_leaf(&self, node: &Value) -> Result<Option<String>, DocumentError> {
40        let Kind::Scalar(scalar) = &node.kind else {
41            return Ok(None);
42        };
43        let tokens = Lexer::tokenize(scalar.text())?;
44        match tokens.into_iter().next().map(|token| token.kind) {
45            Some(TokenKind::String(Some(text))) => Ok(Some(text)),
46            Some(TokenKind::String(None)) => Err(JsonError {
47                offset: scalar.offset(),
48                kind: JsonErrorKind::InvalidEscape,
49            }
50            .into()),
51            _ => Ok(None),
52        }
53    }
54
55    fn finish(&self, current: &mut Document, desired: &Document, existing: Existing) {
56        current.apply_header(desired, existing);
57        current.end_with_newline();
58    }
59}