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