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