1use alloc::borrow::Cow;
2use core::panic::Location;
3
4use crate::{
5 Config, GErrDefault, IdSource, gerr,
6 gerr::{ErrorLocation, GErr, Source},
7 gerr_source::{DataSource, GErrSource},
8 gerr_view::GErrView,
9};
10
11#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
13pub struct DisplayJsonData {
14 pub id: Option<serde_json::Value>,
16 pub code: Option<String>,
18 pub message: String,
20 pub tags: Option<Vec<String>>,
22 pub data: Option<serde_json::Value>,
24 pub help: Option<String>,
26 pub causes: Option<Vec<DisplayCausesJsonData>>,
28}
29
30#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
32pub struct DisplayCausesJsonData {
33 pub message: String,
35 pub caused_by: Option<Vec<DisplayCausesJsonData>>,
37}
38
39#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
41pub struct JsonData {
42 pub id: Option<serde_json::Value>,
44 pub code: Option<String>,
46 pub message: String,
48 pub tags: Option<Vec<String>>,
50 pub data: Option<serde_json::Value>,
52 pub location: Option<LocationJsonData>,
54 pub sources: Option<Vec<SourceJsonData>>,
56 pub help: Option<String>,
58
59 pub backtrace: Option<String>,
61}
62
63#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
65pub struct LocationJsonData {
66 pub file: String,
68 pub line: u32,
70 pub column: u32,
72}
73
74#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize, Default)]
76pub struct SourceJsonData {
77 pub id: Option<serde_json::Value>,
79 pub code: Option<String>,
81 pub message: String,
83 pub tags: Option<Vec<String>>,
85 pub data: Option<serde_json::Value>,
87 pub location: Option<LocationJsonData>,
89 pub sources: Option<Vec<SourceJsonData>>,
91 pub help: Option<String>,
93}
94
95impl<'a, C: Config, D> From<&GErrView<'a, C, D>> for JsonData
96where
97 C::Id: ::serde::Serialize,
98 D: ::serde::Serialize,
99{
100 fn from(err: &GErrView<'a, C, D>) -> Self {
101 JsonData {
102 id: err
103 .id
104 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
105 code: err.code.map(|s| s.into()),
106 message: err.message.into(),
107 tags: err.tags.map(|t| t.iter().map(|s| s.to_string()).collect()),
108 data: err
109 .data
110 .map(|d| serde_json::to_value(d).unwrap_or_default()),
111 help: err.help.map(Into::into),
112 location: Some(LocationJsonData {
113 file: err.location.file.to_string(),
114 line: err.location.line,
115 column: err.location.column,
116 }),
117 sources: err.sources.map(|s| s.iter().map(|i| i.into()).collect()),
118 #[cfg(not(feature = "backtrace"))]
119 backtrace: None,
120 #[cfg(feature = "backtrace")]
121 backtrace: match err.backtrace.status() {
122 std::backtrace::BacktraceStatus::Disabled => Some("<disabled>".into()),
123 std::backtrace::BacktraceStatus::Captured => Some(err.backtrace.to_string()),
124 _ => Some("<unsupported>".into()),
125 },
126 }
127 }
128}
129
130impl<'a, C: Config, D> From<&GErrView<'a, C, D>> for DisplayJsonData
131where
132 C::Id: ::serde::Serialize,
133 D: ::serde::Serialize,
134{
135 fn from(err: &GErrView<'a, C, D>) -> Self {
136 DisplayJsonData {
137 id: err
138 .id
139 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
140 code: err.code.map(|s| s.into()),
141 message: err.message.into(),
142 tags: err.tags.map(|t| t.iter().map(|s| s.to_string()).collect()),
143 data: err
144 .data
145 .map(|d| serde_json::to_value(d).unwrap_or_default()),
146 help: err.help.map(Into::into),
147 causes: err
148 .sources
149 .map(|sources| sources.iter().map(|src| src.into()).collect::<Vec<_>>()),
150 }
151 }
152}
153
154impl<C: Config, D> TryFrom<JsonData> for GErr<C, D>
159where
160 C::Id: for<'a> ::serde::Deserialize<'a>,
161 D: for<'a> ::serde::Deserialize<'a>,
162{
163 type Error = GErrDefault;
164
165 fn try_from(value: JsonData) -> Result<Self, Self::Error> {
166 let JsonData {
167 id,
168 code,
169 message,
170 tags,
171 data,
172 help,
173 location,
174 sources,
175 backtrace: _,
176 } = value;
177
178 let de_id: Option<C::Id> = if let Some(the_id) = id {
179 serde_json::from_value(the_id).map_err(|err| {
180 gerr!(
181 "failed converting id to ID = {}",
182 core::any::type_name::<C::Id>()
183 )
184 .add_source(err)
185 })?
186 } else {
187 C::id()
188 };
189
190 let mut err = GErr::<C, D>::new_with_id_untracked(de_id, message, Location::caller());
191
192 if let Some(data) = data {
193 err = err.set_data(serde_json::from_value(data).map_err(|err| {
194 gerr!(
195 "failed converting data into D = {}",
196 core::any::type_name::<D>()
197 )
198 .add_source(err)
199 })?);
200 }
201
202 if let Some(code) = code {
203 err = err.set_code(code);
204 } else if let Some(const_code) = C::CODE {
205 err = err.set_code(const_code);
206 }
207
208 if let Some(tags) = tags {
209 err = err.add_tags(tags);
210 }
211
212 if let Some(sources) = sources {
213 let gerr_sources: Vec<Source> = sources.into_iter().map(|s| s.into_source()).collect();
214 err = err.set_sources(gerr_sources);
215 }
216
217 if let Some(help) = help {
218 err = err.set_help(help);
219 }
220
221 if let Some(loc) = location {
222 err = err.set_location(ErrorLocation {
223 file: Cow::Owned(loc.file),
224 line: loc.line,
225 column: loc.column,
226 });
227 }
228
229 Ok(err)
230 }
231}
232
233impl From<&Source> for SourceJsonData {
234 fn from(gerr: &Source) -> Self {
235 match gerr {
236 Source::Err(err) => Self {
237 message: err.to_string(),
238 ..Default::default()
239 },
240 Source::GErr(gerr) => Self {
241 id: serde_json::from_value({
242 match &gerr.id_json {
243 Some(::serde_json::Value::Number(num)) => {
244 ::serde_json::Value::from(num.as_i64().unwrap_or_default())
245 }
246 Some(::serde_json::Value::String(s)) => {
247 ::serde_json::Value::from(s.as_str())
248 }
249 Some(::serde_json::Value::Bool(b)) => ::serde_json::Value::from(*b),
250 Some(::serde_json::Value::Array(arr)) => {
251 ::serde_json::Value::from(arr.as_slice())
252 }
253 Some(::serde_json::Value::Object(obj)) => {
254 ::serde_json::Value::from(obj.clone())
255 }
256 _ => ::serde_json::Value::Null,
257 }
258 })
259 .unwrap_or_default(),
260 code: gerr.code.as_deref().map(|s| s.into()),
261 message: gerr.message.to_string(),
262 tags: gerr
263 .tags
264 .as_ref()
265 .map(|t| t.iter().map(|t| t.to_string()).collect()),
266 data: serde_json::from_value({
267 if let Some(json) = &gerr.data_json {
268 match json {
269 ::serde_json::Value::Bool(b) => ::serde_json::Value::from(*b),
270 ::serde_json::Value::Number(num) => {
271 ::serde_json::Value::from(num.as_i64().unwrap_or_default())
272 }
273 ::serde_json::Value::String(s) => ::serde_json::Value::from(s.as_str()),
274 ::serde_json::Value::Array(arr) => {
275 let slice: &[::serde_json::Value] = arr.as_ref();
276 ::serde_json::Value::from(slice)
277 }
278 serde_json::Value::Object(obj) => {
279 ::serde_json::Value::Object(obj.clone())
280 }
281 _ => ::serde_json::Value::Null,
282 }
283 } else {
284 ::serde_json::Value::Null
285 }
286 })
287 .unwrap_or_default(),
288 help: gerr.help.as_deref().map(|s| s.into()),
289 sources: gerr
290 .sources
291 .as_ref()
292 .map(|s| s.iter().map(|s| s.into()).collect()),
293 location: gerr.location.as_ref().map(|loc| LocationJsonData {
294 file: loc.file.to_string(),
295 line: loc.line,
296 column: loc.column,
297 }),
298 },
299 }
300 }
301}
302
303impl From<&Source> for DisplayCausesJsonData {
304 fn from(value: &Source) -> Self {
305 match value {
306 Source::Err(err) => Self {
307 message: err.to_string(),
308 caused_by: None,
309 },
310 Source::GErr(gerr) => Self {
311 message: gerr.to_string(),
312 caused_by: gerr
313 .sources
314 .as_deref()
315 .map(|s| s.iter().map(|i| i.into()).collect::<Vec<_>>()),
316 },
317 }
318 }
319}
320
321impl SourceJsonData {
322 fn into_source(self) -> Source {
323 let SourceJsonData {
324 id,
325 code,
326 message,
327 tags,
328 data,
329 help,
330 location,
331 sources,
332 } = self;
333
334 let gerr_source = GErrSource {
335 id: match id {
336 Some(serde_json::Value::Bool(b)) => Some(Box::new(b)),
337 Some(serde_json::Value::Number(ref num)) => {
338 Some(Box::new(num.as_i64().unwrap_or_default()))
339 }
340 _ => id
341 .as_ref()
342 .map(|id| Box::new(id.to_string()) as Box<dyn IdSource>),
343 },
344
345 id_json: id,
346
347 message: message.into(),
348
349 code: code.map(Cow::Owned),
350
351 sources: sources.map(|s| s.into_iter().map(|sj| sj.into_source()).collect()),
352
353 tags: tags.map(|tags| tags.into_iter().map(Cow::Owned).collect()),
354
355 data: data
356 .as_ref()
357 .map(|v| Box::new(v.to_string()) as Box<dyn DataSource>),
358
359 data_json: data,
360
361 help: help.map(Cow::Owned),
362
363 location: location.map(|loc| ErrorLocation {
364 file: Cow::Owned(loc.file),
365 line: loc.line,
366 column: loc.column,
367 }),
368 };
369
370 Source::GErr(Box::new(gerr_source))
371 }
372}