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 }