1use crate::gerr::Source;
2use crate::report::json_data::{DisplayJsonData, JsonData};
3use crate::{Config, gerr_view::GErrView, report::Report};
4use alloc::borrow::Cow;
5use core::fmt::{Debug, Display};
6
7pub struct JsonReport;
9
10impl JsonReport {
11 pub fn data<E, C: Config, D>(err: &E) -> JsonData
13 where
14 for<'a> &'a E: Into<GErrView<'a, C, D>>,
15 C::Id: serde::Serialize,
16 D: serde::Serialize,
17 {
18 let err = &err.into();
19 err.into()
20 }
21
22 pub fn display_data<E, C: Config, D>(err: &E) -> DisplayJsonData
24 where
25 for<'a> &'a E: Into<GErrView<'a, C, D>>,
26 C::Id: serde::Serialize,
27 D: serde::Serialize,
28 {
29 let err = &err.into();
30 err.into()
31 }
32}
33
34impl Report for JsonReport {
35 fn report<E, C: Config, D>(err: &E) -> String
36 where
37 for<'a> &'a E: Into<GErrView<'a, C, D>>,
38 C::Id: Display + serde::Serialize,
39 D: Debug + serde::Serialize,
40 {
41 let err = &err.into();
42 let resp: JsonReportData = err.into();
43 serde_json::to_string_pretty(&resp).unwrap_or("<invalid json format>".into())
44 }
45}
46
47#[derive(serde::Serialize)]
48struct JsonReportData<'a> {
49 pub id: Option<serde_json::Value>,
50 pub code: Option<&'a str>,
51 pub message: &'a str,
52 pub tags: Option<&'a [Cow<'static, str>]>,
53 pub data: Option<serde_json::Value>,
54 pub location: LocationJson<'a>,
55 pub sources: Option<Vec<SourceJson<'a>>>,
56 pub help: Option<&'a str>,
57
58 #[cfg(feature = "backtrace")]
59 pub backtrace: String,
60}
61
62#[derive(serde::Serialize)]
63struct LocationJson<'a> {
64 pub file: &'a str,
65 pub line: u32,
66 pub column: u32,
67}
68
69#[derive(serde::Serialize)]
70struct SourceJson<'a> {
71 pub id: Option<&'a serde_json::Value>,
72 pub code: Option<&'a str>,
73 pub message: String,
74 pub tags: Option<&'a [Cow<'static, str>]>,
75 pub data: Option<&'a serde_json::Value>,
76 pub location: Option<LocationJson<'a>>,
77 pub sources: Option<Vec<SourceJson<'a>>>,
78 pub help: Option<&'a str>,
79}
80
81impl<'a, C: Config, D> From<&'a GErrView<'a, C, D>> for JsonReportData<'a>
82where
83 C::Id: serde::Serialize,
84 D: serde::Serialize,
85{
86 fn from(value: &'a GErrView<C, D>) -> Self {
87 Self {
88 id: value
89 .id
90 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
91 code: value.code,
92 message: value.message,
93 tags: value.tags,
94 data: serde_json::to_value(value.data).ok(),
95 location: LocationJson {
96 file: &value.location.file,
97 line: value.location.line,
98 column: value.location.column,
99 },
100 sources: value.sources.map(|s| s.iter().map(|v| v.into()).collect()),
101 help: value.help,
102
103 #[cfg(feature = "backtrace")]
104 backtrace: format!("{:#?}", value.backtrace),
105 }
106 }
107}
108
109pub static NO_ID_JSON: serde_json::Value = serde_json::Value::Null;
110
111impl<'a> From<&'a Source> for SourceJson<'a> {
112 fn from(source: &'a Source) -> Self {
113 match source {
114 Source::Err(err) => Self {
115 id: Some(&NO_ID_JSON),
116 code: None,
117 message: err.to_string(),
118 tags: None,
119 data: None,
120 location: None,
121 sources: None,
122 help: None,
123 },
124 Source::GErr(gerr) => Self {
125 id: gerr.id_json.as_ref(),
126 code: gerr.code.as_deref(),
127 message: gerr.message.to_string(),
128 tags: gerr.tags.as_deref(),
129 data: gerr.data_json.as_ref(),
130 location: gerr.location.as_ref().map(|loc| LocationJson {
131 file: &loc.file,
132 line: loc.line,
133 column: loc.column,
134 }),
135 sources: gerr
136 .sources
137 .as_deref()
138 .map(|s| s.iter().map(|src| src.into()).collect()),
139 help: gerr.help.as_deref(),
140 },
141 }
142 }
143}