facet_json/deserialize/
error.rs1#[cfg(feature = "alloc")]
2use alloc::format;
3#[cfg(feature = "alloc")]
4use alloc::string::String;
5
6#[cfg(feature = "rich-diagnostics")]
7use ariadne::{Color, Config, IndexType, Label, Report, ReportKind, Source};
8#[cfg(feature = "rich-diagnostics")]
9use owo_colors::OwoColorize;
10
11pub struct JsonParseErrorWithContext<'input> {
13 #[cfg_attr(not(feature = "rich-diagnostics"), allow(dead_code))]
14 input: &'input [u8],
15 pos: usize,
16 pub kind: JsonErrorKind,
18 path: String,
19}
20
21impl<'input> JsonParseErrorWithContext<'input> {
22 pub fn new(kind: JsonErrorKind, input: &'input [u8], pos: usize, path: String) -> Self {
30 Self {
31 input,
32 pos,
33 kind,
34 path,
35 }
36 }
37
38 pub fn message(&self) -> String {
40 match &self.kind {
41 JsonErrorKind::UnexpectedEof(msg) => format!("Unexpected end of file: {}", msg),
42 JsonErrorKind::MissingField(fld) => format!("Missing required field: {}", fld),
43 JsonErrorKind::UnexpectedCharacter(c) => format!("Unexpected character: '{}'", c),
44 JsonErrorKind::NumberOutOfRange(n) => format!("Number out of range: {}", n),
45 JsonErrorKind::StringAsNumber(s) => format!("Expected a string but got number: {}", s),
46 JsonErrorKind::UnknownField(f) => format!("Unknown field: {}", f),
47 JsonErrorKind::InvalidUtf8(e) => format!("Invalid UTF-8 encoding: {}", e),
48 }
49 }
50}
51
52#[derive(Debug)]
54pub enum JsonErrorKind {
55 UnexpectedEof(&'static str),
57 MissingField(&'static str),
59 UnexpectedCharacter(char),
61 NumberOutOfRange(f64),
63 StringAsNumber(String),
65 UnknownField(String),
67 InvalidUtf8(String),
69}
70
71#[cfg(not(feature = "rich-diagnostics"))]
72impl core::fmt::Display for JsonParseErrorWithContext<'_> {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 write!(
75 f,
76 "{} at byte {} in path {}",
77 self.message(),
78 self.pos,
79 self.path
80 )
81 }
82}
83
84#[cfg(feature = "rich-diagnostics")]
85impl core::fmt::Display for JsonParseErrorWithContext<'_> {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 let Ok(input_str) = core::str::from_utf8(self.input) else {
88 return write!(f, "(JSON input was invalid UTF-8)");
89 };
90
91 let source_id = "json";
92
93 let (span_start, span_end) = match &self.kind {
94 JsonErrorKind::StringAsNumber(s) => (self.pos - s.len(), self.pos),
95 JsonErrorKind::UnknownField(f) => (self.pos - f.len() - 1, self.pos - 1),
96 _ => {
97 let span_end = if self.pos < self.input.len() {
98 self.pos + 1
99 } else {
100 self.input.len()
101 };
102 (self.pos, span_end)
103 }
104 };
105
106 let mut report = Report::build(ReportKind::Error, (source_id, span_start..span_end))
107 .with_message(format!("Error at {}", self.path.yellow()))
108 .with_config(Config::new().with_index_type(IndexType::Byte));
109
110 let label = Label::new((source_id, span_start..span_end))
111 .with_message(self.message())
112 .with_color(Color::Red);
113
114 report = report.with_label(label);
115
116 let source = Source::from(input_str);
117
118 let mut writer = Vec::new();
119 let cache = (source_id, &source);
120
121 if report.finish().write(cache, &mut writer).is_err() {
122 return write!(f, "Error formatting with ariadne");
123 }
124
125 if let Ok(output) = String::from_utf8(writer) {
126 write!(f, "{}", output)
127 } else {
128 write!(f, "Error converting ariadne output to string")
129 }
130 }
131}
132
133impl core::fmt::Debug for JsonParseErrorWithContext<'_> {
134 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135 core::fmt::Display::fmt(self, f)
136 }
137}
138
139impl core::error::Error for JsonParseErrorWithContext<'_> {}