turbo_tasks_fs/
json.rs

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