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, filesCss;
8 import dextool.plugin.mutate.backend.resource;
9 
10 /** A JSON-like object that is more easily manipulated than JSONValue.
11 *
12 * Has operator overloads for "index", "index assign" and "in" allowing the
13 * children list to be private and cleaner construction of the object.
14 */
15 auto makeTreeMapPage(FileIndex[] files) {
16     import std.format : format;
17 
18     auto doc = tmplBasicPage.filesCss;
19     doc.mainBody.setAttribute("onload", "init()");
20 
21     auto s = doc.root.childElements("head")[0].addChild("script");
22     s.addChild(new RawSource(doc, jsD3Mini));
23 
24     doc.mainBody.addChild("div").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, jsTreeMap));
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.stat.total;
54         parent.score = f.stat.score;
55     }
56     return root.toJSONValue().toPrettyString();
57 }
58 
59 class JSONLike {
60     import std.array : empty;
61     import std.format : format;
62     import std.typecons : Nullable;
63 
64     string name;
65     private JSONLike[string] children;
66     Nullable!long locs;
67     Nullable!double score;
68 
69     JSONValue toJSONValue() {
70         JSONValue root = ["name" : name];
71 
72         if (!children.empty) {
73             JSONValue[] descendants = [];
74             foreach (c; children) {
75                 descendants ~= c.toJSONValue();
76             }
77             root.object["children"] = JSONValue(descendants);
78         }
79         if (!locs.isNull)
80             root.object["locs"] = JSONValue(locs.get);
81 
82         if (!score.isNull)
83             root.object["score"] = JSONValue(format("%.2s", score.get));
84 
85         return root;
86     }
87 
88     JSONLike opIndex(string i) pure nothrow {
89         return children[i];
90     }
91 
92     JSONLike opIndexAssign(JSONLike value, string i) pure nothrow {
93         children[i] = value;
94         return value;
95     }
96 
97     bool opBinaryRight(string op)(string a) {
98         static if (op == "in") {
99             if (a in children) {
100                 return true;
101             }
102             return false;
103         } else
104             static assert(0, "Operator " ~ op ~ " not implmeneted");
105     }
106 }