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 }