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 : ReportLevel, 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 ReportSection[] toSections(const ReportLevel l) @safe {
50     ReportSection[] secs;
51     final switch (l) with (ReportSection) {
52     case ReportLevel.summary:
53         secs = [summary];
54         break;
55     case ReportLevel.alive:
56         secs = [summary, mut_stat, tc_killed_no_mutants, tc_full_overlap, alive];
57         break;
58     case ReportLevel.all:
59         secs = [
60             summary, mut_stat, all_mut, tc_killed, tc_killed_no_mutants,
61             tc_full_overlap
62         ];
63         break;
64     }
65 
66     return secs;
67 }
68 
69 string toInternal(ubyte[] data) @safe nothrow {
70     import std.utf : validate;
71 
72     try {
73         auto result = () @trusted { return cast(string) data; }();
74         validate(result);
75         return result;
76     } catch (Exception e) {
77     }
78 
79     return invalidFile;
80 }
81 
82 struct Table(int columnsNr) {
83     alias Row = string[columnsNr];
84 
85     Row heading_;
86     Row[] rows;
87     ulong[columnsNr] columnWidth;
88 
89     this(const Row heading) @safe {
90         this.heading = heading;
91         updateColumns(heading);
92     }
93 
94     bool empty() @safe pure nothrow const @nogc {
95         return rows.length == 0;
96     }
97 
98     void heading(const Row r) @safe {
99         heading_ = r;
100         updateColumns(r);
101     }
102 
103     void put(const Row r) @safe {
104         rows ~= r;
105         updateColumns(r);
106     }
107 
108     import std.format : FormatSpec;
109 
110     void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
111         import std.ascii : newline;
112         import std.range : enumerate, repeat;
113         import std.format : formattedWrite;
114         import std.range.primitives : put;
115 
116         immutable sep = "|";
117         immutable lhs_sep = "| ";
118         immutable mid_sep = " | ";
119         immutable rhs_sep = " |";
120 
121         void printRow(const ref Row r) {
122             foreach (const r_; r[].enumerate) {
123                 if (r_.index == 0)
124                     put(w, lhs_sep);
125                 else
126                     put(w, mid_sep);
127                 formattedWrite(w, "%-*s", columnWidth[r_.index], r_.value);
128             }
129             put(w, rhs_sep);
130             put(w, newline);
131         }
132 
133         printRow(heading_);
134 
135         immutable dash = "-";
136         foreach (len; columnWidth) {
137             put(w, sep);
138             put(w, repeat(dash, len + 2));
139         }
140         put(w, sep);
141         put(w, newline);
142 
143         foreach (const ref r; rows) {
144             printRow(r);
145         }
146     }
147 
148     private void updateColumns(const ref Row r) @safe {
149         import std.algorithm : filter, count, map;
150         import std.range : enumerate;
151         import std.utf : byCodeUnit;
152         import std.typecons : tuple;
153 
154         foreach (a; r[].enumerate
155                 .map!(a => tuple(a.index, a.value.byCodeUnit.count))
156                 .filter!(a => a[1] > columnWidth[a[0]])) {
157             columnWidth[a[0]] = a[1];
158         }
159     }
160 }
161 
162 string statusToString(Mutation.Status status) @trusted {
163     import std.conv : to;
164 
165     return to!string(status);
166 }
167 
168 string statusToString(ulong status) @trusted {
169     import std.conv : to;
170 
171     return statusToString(status.to!(Mutation.Status));
172 }
173 
174 string kindToString(Mutation.Kind kind) @trusted {
175     import std.conv : to;
176 
177     return to!string(kind);
178 }
179 
180 string kindToString(long kind) @trusted {
181     import std.conv : to;
182 
183     return kindToString(kind.to!(Mutation.Kind));
184 }