// JSON 输出层。给机器读的,所以字段名固定、值不带颜色和对齐这些给人看的东西。
//
// Map 按写入顺序输出,所以字段先后就是下面构造时写的顺序。同一份 diff
// 每次序列化出来的字节都一样,快照测试可以直接写死。
///|
fn jstr(s : String) -> Json {
Json::string(s)
}
///|
fn jint(n : Int) -> Json {
Json::number(n.to_double())
}
///|
fn jbool(b : Bool) -> Json {
Json::boolean(b)
}
///|
fn jopt_int(n : Int?) -> Json {
match n {
None => Json::null()
Some(v) => jint(v)
}
}
///|
fn jopt_str(s : String?) -> Json {
match s {
None => Json::null()
Some(v) => jstr(v)
}
}
///|
fn jarr(items : Array[Json]) -> Json {
Json::array(items)
}
///|
/// 按名字装一个 JSON 对象。
fn jobj(fields : Array[(String, Json)]) -> Json {
let obj : Map[String, Json] = Map([])
for (k, v) in fields {
obj[k] = v
}
Json::object(obj)
}
///|
fn LineKind::kind_name(self : LineKind) -> String {
match self {
Context => "context"
Added => "added"
Removed => "removed"
}
}
///|
fn line_json(l : Line) -> Json {
jobj([
("kind", jstr(l.kind.kind_name())),
("old_line", jopt_int(l.old_lineno)),
("new_line", jopt_int(l.new_lineno)),
("content", jstr(l.content)),
])
}
///|
fn hunk_json(h : Hunk) -> Json {
jobj([
("old_start", jint(h.old_start)),
("old_count", jint(h.old_count)),
("new_start", jint(h.new_start)),
("new_count", jint(h.new_count)),
("heading", jstr(h.heading)),
("no_newline_old", jbool(h.no_newline_old)),
("no_newline_new", jbool(h.no_newline_new)),
("lines", jarr(h.lines.map(line_json))),
])
}
///|
fn file_json(f : FileDiff) -> Json {
jobj([
("old_path", jstr(f.old_path)),
("new_path", jstr(f.new_path)),
("path", jstr(f.display_path())),
("status", jstr(f.status.label())),
("binary", jbool(f.binary)),
("old_mode", jopt_str(f.old_mode)),
("new_mode", jopt_str(f.new_mode)),
("added", jint(f.added_count())),
("removed", jint(f.removed_count())),
("hunks", jarr(f.hunks.map(hunk_json))),
])
}
///|
pub fn Diff::to_json(self : Diff) -> Json {
jobj([
(
"summary",
jobj([
("file_count", jint(self.file_count())),
("hunk_count", jint(self.hunk_count())),
("added", jint(self.added_count())),
("removed", jint(self.removed_count())),
]),
),
("files", jarr(self.files.map(file_json))),
])
}
///|
/// 缩进两格的可读 JSON,给命令行 `--json` 用。
pub fn Diff::format_json(self : Diff) -> String {
self.to_json().stringify(indent=2)
}