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