1use super::{Report, Status, TraceReason, TraceStep};
3use alloc::string::String;
4use elenchus_compiler::{Origin, PlaceholderStatus, Value};
5
6impl Report {
7 pub fn exit_code(&self) -> i32 {
9 match self.status {
10 Status::Conflict => 2,
11 Status::Underdetermined | Status::Warning => 1,
12 Status::Consistent => 0,
13 }
14 }
15
16 pub fn to_json(&self) -> String {
19 use core::fmt::Write as _;
20 let mut s = String::new();
21 let _ = write!(s, "{{\"status\":");
22 status_name(self.status).write_json(&mut s);
23 let _ = write!(s, ",\"exit_code\":{}", self.exit_code());
24
25 s.push_str(",\"conflicts\":[");
26 for (i, c) in self.conflicts.iter().enumerate() {
27 if i > 0 {
28 s.push(',');
29 }
30 json_origin(&c.origin, &mut s);
31 s.push_str(",\"atoms\":");
32 c.atoms.write_json(&mut s);
33 s.push_str(",\"trace\":[");
34 for (j, step) in c.trace.iter().enumerate() {
35 if j > 0 {
36 s.push(',');
37 }
38 step.write_json(&mut s);
39 }
40 s.push_str("]}");
41 }
42 s.push_str("],\"warnings\":[");
43 for (i, w) in self.warnings.iter().enumerate() {
44 if i > 0 {
45 s.push(',');
46 }
47 json_origin(&w.origin, &mut s);
48 s.push_str(",\"blocked_by\":");
49 w.blocked_by.write_json(&mut s);
50 s.push_str(",\"hint\":");
51 match &w.hint {
52 Some(h) => h.write_json(&mut s),
53 None => s.push_str("null"),
54 }
55 s.push('}');
56 }
57 s.push_str("],\"derived\":[");
58 for (i, d) in self.derived.iter().enumerate() {
59 if i > 0 {
60 s.push(',');
61 }
62 s.push('{');
63 json_origin_fields(&d.origin, &mut s);
64 s.push_str(",\"atom\":");
65 d.atom.write_json(&mut s);
66 let _ = write!(s, ",\"value\":{}", matches!(d.value, Value::True));
67 s.push('}');
68 }
69 s.push_str("],\"underdetermined\":");
70 match &self.underdetermined {
71 Some(atom) => atom.write_json(&mut s),
72 None => s.push_str("null"),
73 }
74 s.push_str(",\"unsat_core\":[");
75 for (i, it) in self.unsat_core.iter().enumerate() {
76 if i > 0 {
77 s.push(',');
78 }
79 json_origin(&it.origin, &mut s);
80 s.push_str(",\"label\":");
81 it.label.write_json(&mut s);
82 s.push('}');
83 }
84 s.push_str("],\"retract\":[");
85 for (i, it) in self.retract.iter().enumerate() {
86 if i > 0 {
87 s.push(',');
88 }
89 json_origin(&it.origin, &mut s);
90 s.push_str(",\"label\":");
91 it.label.write_json(&mut s);
92 s.push('}');
93 }
94 s.push_str("],\"hints\":[");
95 for (i, h) in self.hints.iter().enumerate() {
96 if i > 0 {
97 s.push(',');
98 }
99 s.push_str("{\"a\":");
100 h.a.write_json(&mut s);
101 s.push_str(",\"b\":");
102 h.b.write_json(&mut s);
103 s.push_str(",\"reason\":");
104 h.reason.write_json(&mut s);
105 s.push('}');
106 }
107 s.push_str("],\"orphans\":[");
108 for (i, o) in self.orphans.iter().enumerate() {
109 if i > 0 {
110 s.push(',');
111 }
112 json_origin(&o.origin, &mut s);
113 s.push_str(",\"atom\":");
114 o.atom.write_json(&mut s);
115 let _ = write!(s, ",\"value\":{}", matches!(o.value, Value::True));
116 s.push('}');
117 }
118 s.push_str("],\"unused_imports\":[");
119 for (i, u) in self.unused_imports.iter().enumerate() {
120 if i > 0 {
121 s.push(',');
122 }
123 s.push_str("{\"file\":");
124 u.file.write_json(&mut s);
125 s.push_str(",\"domain\":");
126 u.domain.write_json(&mut s);
127 s.push_str(",\"alias\":");
128 match &u.alias {
129 Some(a) => a.write_json(&mut s),
130 None => s.push_str("null"),
131 }
132 let _ = write!(s, ",\"line\":{}", u.line);
133 s.push('}');
134 }
135 s.push_str("],\"placeholders\":[");
136 for (i, p) in self.placeholders.iter().enumerate() {
137 if i > 0 {
138 s.push(',');
139 }
140 s.push_str("{\"key\":");
141 p.key.write_json(&mut s);
142 let status = match p.status {
143 PlaceholderStatus::Supplied => "supplied",
144 PlaceholderStatus::DefaultUsed => "default",
145 PlaceholderStatus::Unset => "unset",
146 };
147 s.push_str(",\"status\":");
148 status.write_json(&mut s);
149 match p.value {
150 Some(v) => {
151 let _ = write!(s, ",\"value\":{v}");
152 }
153 None => s.push_str(",\"value\":null"),
154 }
155 s.push_str(",\"origin\":");
156 match &p.origin {
157 Some(o) => o.write_json(&mut s),
158 None => s.push_str("null"),
159 }
160 s.push('}');
161 }
162 s.push_str("]}");
163 s
164 }
165}
166
167trait ToJson {
171 fn write_json(&self, out: &mut String);
172}
173
174impl ToJson for str {
176 fn write_json(&self, out: &mut String) {
177 use core::fmt::Write as _;
178 out.push('"');
179 for ch in self.chars() {
180 match ch {
181 '"' => out.push_str("\\\""),
182 '\\' => out.push_str("\\\\"),
183 '\n' => out.push_str("\\n"),
184 '\r' => out.push_str("\\r"),
185 '\t' => out.push_str("\\t"),
186 c if (c as u32) < 0x20 => {
187 let _ = write!(out, "\\u{:04x}", c as u32);
188 }
189 c => out.push(c),
190 }
191 }
192 out.push('"');
193 }
194}
195
196impl ToJson for [String] {
198 fn write_json(&self, out: &mut String) {
199 out.push('[');
200 for (i, item) in self.iter().enumerate() {
201 if i > 0 {
202 out.push(',');
203 }
204 item.write_json(out);
205 }
206 out.push(']');
207 }
208}
209
210impl ToJson for TraceStep {
212 fn write_json(&self, out: &mut String) {
213 use core::fmt::Write as _;
214 out.push_str("{\"atom\":");
215 self.atom.write_json(out);
216 let _ = write!(out, ",\"value\":{}", matches!(self.value, Value::True));
217 match &self.reason {
218 TraceReason::Asserted(o) => {
219 out.push_str(",\"how\":\"asserted\",");
220 json_origin_fields(o, out);
221 out.push_str(",\"from\":[]");
222 }
223 TraceReason::Derived { origin, from } => {
224 out.push_str(",\"how\":\"derived\",");
225 json_origin_fields(origin, out);
226 out.push_str(",\"from\":");
227 from.write_json(out);
228 }
229 }
230 out.push('}');
231 }
232}
233
234pub(crate) fn status_name(s: Status) -> &'static str {
235 match s {
236 Status::Consistent => "CONSISTENT",
237 Status::Underdetermined => "UNDERDETERMINED",
238 Status::Warning => "WARNING",
239 Status::Conflict => "CONFLICT",
240 }
241}
242
243pub(crate) fn json_origin_fields(o: &Origin, out: &mut String) {
245 use core::fmt::Write as _;
246 out.push_str("\"premise\":");
247 match &o.premise {
248 Some(name) => name.write_json(out),
249 None => out.push_str("null"),
250 }
251 out.push_str(",\"kind\":");
252 o.kind.write_json(out);
253 out.push_str(",\"source\":");
254 o.source.write_json(out);
255 let _ = write!(out, ",\"line\":{}", o.line);
256}
257
258pub(crate) fn json_origin(o: &Origin, out: &mut String) {
260 out.push('{');
261 json_origin_fields(o, out);
262}