turbo_tasks_fs/
json.rs

1use std::fmt::{Display, Formatter, Write};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{NonLocalValue, trace::TraceRawVcs};
7
8use crate::{rope::Rope, source_context::get_source_context};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
11pub struct UnparsableJson {
12    pub message: RcStr,
13    pub path: Option<RcStr>,
14    /// The start line and column of the error.
15    /// Line and column is 0-based.
16    pub start_location: Option<(u32, u32)>,
17    /// The end line and column of the error.
18    /// Line and column is 0-based.
19    pub end_location: Option<(u32, u32)>,
20}
21
22/// Converts a byte position to a 0-based line and column.
23fn byte_to_location(pos: usize, text: &str) -> (u32, u32) {
24    let text = &text[..pos];
25    let mut lines = text.lines().rev();
26    let last = lines.next().unwrap_or("");
27    let column = last.len();
28    let line = lines.count();
29    (line as u32, column as u32)
30}
31
32impl UnparsableJson {
33    pub fn from_jsonc_error(e: jsonc_parser::errors::ParseError, text: &str) -> Self {
34        Self {
35            message: RcStr::from(e.kind().to_string()),
36            path: None,
37            start_location: Some(byte_to_location(e.range().start, text)),
38            end_location: Some(byte_to_location(e.range().end, text)),
39        }
40    }
41
42    pub fn from_serde_path_to_error(e: serde_path_to_error::Error<serde_json::Error>) -> Self {
43        let inner = e.inner();
44        Self {
45            message: RcStr::from(inner.to_string()),
46            path: Some(RcStr::from(e.path().to_string())),
47            start_location: Some((
48                inner.line().saturating_sub(1) as u32,
49                inner.column().saturating_sub(1) as u32,
50            )),
51            end_location: None,
52        }
53    }
54
55    pub fn write_with_content(&self, writer: &mut impl Write, text: &str) -> std::fmt::Result {
56        writeln!(writer, "{}", self.message)?;
57        if let Some(path) = &self.path {
58            writeln!(writer, "  at {path}")?;
59        }
60        match (self.start_location, self.end_location) {
61            (Some((line, column)), Some((end_line, end_column))) => {
62                write!(
63                    writer,
64                    "{}",
65                    get_source_context(text.lines(), line, column, end_line, end_column,)
66                )?;
67            }
68            (Some((line, column)), None) | (None, Some((line, column))) => {
69                write!(
70                    writer,
71                    "{}",
72                    get_source_context(text.lines(), line, column, line, column)
73                )?;
74            }
75            (None, None) => {
76                write!(writer, "{}", get_source_context(text.lines(), 0, 0, 0, 0))?;
77            }
78        }
79        Ok(())
80    }
81
82    pub fn to_string_with_content(&self, text: &str) -> String {
83        let mut result = String::new();
84        self.write_with_content(&mut result, text).unwrap();
85        result
86    }
87}
88
89impl Display for UnparsableJson {
90    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
91        write!(f, "{}", self.message)?;
92        if let Some(path) = &self.path {
93            write!(f, "  at {path}")?;
94        }
95        Ok(())
96    }
97}
98
99pub fn parse_json_with_source_context<'de, T: Deserialize<'de>>(text: &'de str) -> Result<T> {
100    let de = &mut serde_json::Deserializer::from_str(text);
101    match serde_path_to_error::deserialize(de) {
102        Ok(data) => Ok(data),
103        Err(e) => Err(anyhow::Error::msg(
104            UnparsableJson::from_serde_path_to_error(e).to_string_with_content(text),
105        )),
106    }
107}
108
109pub fn parse_json_rope_with_source_context<'de, T: Deserialize<'de>>(rope: &'de Rope) -> Result<T> {
110    let de = &mut serde_json::Deserializer::from_reader(rope.read());
111    match serde_path_to_error::deserialize(de) {
112        Ok(data) => Ok(data),
113        Err(e) => {
114            let cow = rope.to_str()?;
115            Err(anyhow::Error::msg(
116                UnparsableJson::from_serde_path_to_error(e).to_string_with_content(&cow),
117            ))
118        }
119    }
120}