1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 This module contains utility functions used by different reports and analyzers
11 such as type converters, string manipulations etc.
12 */
13 module dextool.plugin.mutate.backend.report.utility;
14 
15 import logger = std.experimental.logger;
16 import std.exception : collectException;
17 import std.typecons : Flag, Yes, No;
18 
19 import dextool.plugin.mutate.backend.database : Database, spinSql, MutationId;
20 import dextool.plugin.mutate.backend.diff_parser : Diff;
21 import dextool.plugin.mutate.backend.interface_ : FilesysIO;
22 import dextool.plugin.mutate.backend.type : Mutation, Offset, TestCase, Language, TestGroup;
23 import dextool.plugin.mutate.type : ReportKillSortOrder;
24 import dextool.plugin.mutate.type : ReportSection;
25 import dextool.type;
26 
27 // 5 because it covers all the operators and true/false
28 immutable windowSize = 5;
29 
30 immutable invalidFile = "Dextool: Invalid UTF-8 content";
31 
32 /// Create a range from `a` that has at most maxlen+3 letters in it.
33 string window(T)(T a, size_t maxlen = windowSize) {
34     import std.algorithm : filter, among;
35     import std.conv : text;
36     import std.range : take, chain;
37     import std.uni : byGrapheme, byCodePoint;
38     import dextool.plugin.mutate.backend.type : invalidUtf8;
39 
40     try {
41         return chain(a.byGrapheme.take(maxlen)
42                 .byCodePoint.filter!(a => !a.among('\n')).text, a.length > maxlen ? "..." : null)
43             .text;
44     } catch (Exception e) {
45         return invalidUtf8;
46     }
47 }
48 
49 string toInternal(ubyte[] data) @safe nothrow {
50     import std.utf : validate;
51 
52     try {
53         auto result = () @trusted { return cast(string) data; }();
54         validate(result);
55         return result;
56     } catch (Exception e) {
57     }
58 
59     return invalidFile;
60 }
61 
62 struct Table(int columnsNr) {
63     import std.format : FormatSpec;
64 
65     alias Row = string[columnsNr];
66 
67     Row heading_;
68     Row[] rows;
69     ulong[columnsNr] columnWidth;
70 
71     this(const Row heading) @safe {
72         this.heading = heading;
73         updateColumns(heading);
74     }
75 
76     bool empty() @safe pure nothrow const @nogc {
77         return rows.length == 0;
78     }
79 
80     void heading(const Row r) @safe {
81         heading_ = r;
82         updateColumns(r);
83     }
84 
85     void put(const Row r) @safe {
86         rows ~= r;
87         updateColumns(r);
88     }
89 
90     void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
91         import std.ascii : newline;
92         import std.range : enumerate, repeat;
93         import std.format : formattedWrite;
94         import std.range.primitives : put;
95 
96         immutable sep = "|";
97         immutable lhs_sep = "| ";
98         immutable mid_sep = " | ";
99         immutable rhs_sep = " |";
100 
101         void printRow(const ref Row r) {
102             foreach (const r_; r[].enumerate) {
103                 if (r_.index == 0)
104                     put(w, lhs_sep);
105                 else
106                     put(w, mid_sep);
107                 formattedWrite(w, "%-*s", columnWidth[r_.index], r_.value);
108             }
109             put(w, rhs_sep);
110             put(w, newline);
111         }
112 
113         printRow(heading_);
114 
115         immutable dash = "-";
116         foreach (len; columnWidth) {
117             put(w, sep);
118             put(w, repeat(dash, len + 2));
119         }
120         put(w, sep);
121         put(w, newline);
122 
123         foreach (const ref r; rows) {
124             printRow(r);
125         }
126     }
127 
128     private void updateColumns(const ref Row r) @safe {
129         import std.algorithm : filter, count, map;
130         import std.range : enumerate;
131         import std.utf : byCodeUnit;
132         import std.typecons : tuple;
133 
134         foreach (a; r[].enumerate
135                 .map!(a => tuple(a.index, a.value.byCodeUnit.count))
136                 .filter!(a => a[1] > columnWidth[a[0]])) {
137             columnWidth[a[0]] = a[1];
138         }
139     }
140 }
141 
142 string statusToString(Mutation.Status status) @trusted {
143     import std.conv : to;
144 
145     return to!string(status);
146 }
147 
148 string statusToString(ulong status) @trusted {
149     import std.conv : to;
150 
151     return statusToString(status.to!(Mutation.Status));
152 }
153 
154 string kindToString(Mutation.Kind kind) @trusted {
155     import std.conv : to;
156 
157     return to!string(kind);
158 }
159 
160 string kindToString(long kind) @trusted {
161     import std.conv : to;
162 
163     return kindToString(kind.to!(Mutation.Kind));
164 }