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 }