1 module dextool.plugin.mutate.backend.report.html.page_tree_map; 2 3 import std.json : JSONValue; 4 import arsd.dom : Document, Element, require, Table, RawSource; 5 6 import dextool.plugin.mutate.backend.report.html : FileIndex; 7 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage; 8 9 /* 10 * A JSON-like object that is more easily manipulated than JSONValue. 11 * Has operator overloads for "index", "index assign" and "in" allowing the children list to be private 12 * and cleaner construction of the object. 13 */ 14 auto makeTreeMapPage(FileIndex[] files) { 15 import std.format : format; 16 17 auto doc = tmplBasicPage; 18 doc.mainBody.setAttribute("onload", "init()"); 19 20 auto s = doc.root.childElements("head")[0].addChild("script"); 21 s.addChild(new RawSource(doc, import("d3.min.js"))); 22 23 auto container = doc.mainBody.addChild("div"); 24 container.setAttribute("id", "container"); 25 26 auto s2 = doc.mainBody.addChild("script"); 27 s2.addChild(new RawSource(doc, format("const g_indata = %s", makeTreeMapJSON(files)))); 28 s2.addChild(new RawSource(doc, import("treemap.js"))); 29 30 return doc.toPrettyString; 31 } 32 33 private: 34 35 auto makeTreeMapJSON(FileIndex[] files) { 36 import std.array : array; 37 import std.path : pathSplitter; 38 39 auto root = new JSONLike(); 40 root.name = "root"; 41 42 foreach (f; files) { 43 auto path = pathSplitter(f.display); 44 auto parent = root; 45 foreach (seg; path) { 46 if (seg !in parent) { 47 auto child = new JSONLike(); 48 child.name = seg; 49 parent[seg] = child; 50 } 51 parent = parent[seg]; 52 } 53 parent.locs = f.totalMutants; 54 if (f.totalMutants == 0) 55 parent.score = 1.0; 56 else 57 parent.score = cast(double) f.killedMutants / cast(double) f.totalMutants; 58 } 59 return root.toJSONValue().toPrettyString(); 60 } 61 62 class JSONLike { 63 import std.array : empty; 64 import std.format : format; 65 import std.typecons : Nullable; 66 67 string name; 68 private JSONLike[string] children; 69 Nullable!long locs; 70 Nullable!double score; 71 72 JSONValue toJSONValue() { 73 JSONValue root = ["name" : name]; 74 75 if (!children.empty) { 76 JSONValue[] descendants = []; 77 foreach (c; children) { 78 descendants ~= c.toJSONValue(); 79 } 80 root.object["children"] = JSONValue(descendants); 81 } 82 if (!locs.isNull) 83 root.object["locs"] = JSONValue(locs.get); 84 85 if (!score.isNull) 86 root.object["score"] = JSONValue(format("%.2s", score.get)); 87 88 return root; 89 } 90 91 JSONLike opIndex(string i) pure nothrow { 92 return children[i]; 93 } 94 95 JSONLike opIndexAssign(JSONLike value, string i) pure nothrow { 96 children[i] = value; 97 return value; 98 } 99 100 bool opBinaryRight(string op)(string a) { 101 static if (op == "in") { 102 if (a in children) { 103 return true; 104 } 105 return false; 106 } else 107 static assert(0, "Operator " ~ op ~ " not implmeneted"); 108 } 109 }