///|
/// Deterministic JSON report. Logical field values require an explicit opt-in.
pub fn report_json(result : ParseResult, show_values? : Bool = false) -> String {
let segments : Array[Json] = []
let messages : Array[Json] = []
let mut profile = Json::null()
if result.document is Some(doc) {
let p = doc.profile
profile = Json::object(
Map([
("version", p.version.to_json()),
("charset", p.charset.to_json()),
("explicit_una", p.explicit_una.to_json()),
(
"service_characters",
Json::object(
Map([
("component", p.component.to_int().to_json()),
("element", p.element.to_int().to_json()),
("decimal", p.decimal.to_int().to_json()),
("release", p.release.to_int().to_json()),
("reserved", p.reserved.to_int().to_json()),
("terminator", p.terminator.to_int().to_json()),
]),
),
),
]),
)
for s in doc.segments {
let elements : Array[Json] = []
for e in s.elements {
let components : Array[Json] = []
for c in e.components {
let fields = Map([
("span", span_json(c.span)),
("length", c.value.length().to_json()),
])
if show_values {
fields["value"] = ascii_text(c.value, 0, c.value.length()).to_json()
}
components.push(Json::object(fields))
}
elements.push(
Json::object(
Map([
("span", span_json(e.span)),
("components", Json::array(components)),
]),
),
)
}
segments.push(
Json::object(
Map([
("index", s.index.to_json()),
("tag", s.tag.to_json()),
("span", span_json(s.span)),
("elements", Json::array(elements)),
]),
),
)
}
}
if result.interchange is Some(exchange) {
for m in exchange.messages {
messages.push(
Json::object(
Map([
("first_segment", m.first_segment.to_json()),
("last_segment", m.last_segment.to_json()),
("span", span_json(m.span)),
]),
),
)
}
}
let diagnostics = result.diagnostics.map(d => {
Json::object(
Map([
("code", d.code.to_json()),
("severity", d.severity.to_json()),
("layer", layer_name(d.layer).to_json()),
("span", span_json(d.span)),
("segment_index", d.segment_index.to_json()),
("message", d.message.to_json()),
("hint", d.hint.to_json()),
]),
)
})
let report = Json::object(
Map([
("schema_version", (1).to_json()),
("syntax", @json.to_json(status_name(result.syntax))),
("envelope", status_name(result.envelope).to_json()),
("rules", status_name(result.rules).to_json()),
("profile", profile),
("segment_count", segments.length().to_json()),
("message_count", messages.length().to_json()),
("segments", Json::array(segments)),
("messages", Json::array(messages)),
("diagnostics", Json::array(diagnostics)),
]),
)
report.stringify()
}
///|
fn span_json(span : SourceSpan) -> Json {
Json::object(
Map([("start", span.start.to_json()), ("end", span.end.to_json())]),
)
}
///|
fn status_name(status : CheckStatus) -> String {
match status {
Passed => "passed"
Failed => "failed"
NotChecked => "not_checked"
}
}
///|
fn layer_name(layer : Layer) -> String {
match layer {
Syntax => "syntax"
Envelope => "envelope"
Resource => "resource"
}
}
///|
/// A concise summary and source diagnostics, without logical input values.
pub fn report_text(result : ParseResult) -> String {
let segments = match result.document {
Some(d) => d.segments.length()
None => 0
}
let messages = match result.interchange {
Some(i) => i.messages.length()
None => 0
}
let text = StringBuilder()
text.write_string(
"syntax=" +
status_name(result.syntax) +
" envelope=" +
status_name(result.envelope) +
" rules=" +
status_name(result.rules) +
" messages=" +
messages.to_string() +
" segments=" +
segments.to_string(),
)
for d in result.diagnostics {
text.write_string(
"\n" +
d.code +
" [" +
d.span.start.to_string() +
"," +
d.span.end.to_string() +
")",
)
if d.segment_index is Some(index) {
text.write_string(" segment=" + index.to_string())
}
text.write_string(" " + d.message)
}
text.to_string()
}
///|
/// True only when syntax and service envelopes passed. This does not check business rules.
pub fn ParseResult::is_valid(self : ParseResult) -> Bool {
self.syntax == Passed && self.envelope == Passed
}