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 }