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 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 string toInternal(ubyte[] data) @safe nothrow { 50 import std.utf : validate; 51 52 try { 53 auto result = () @trusted { return cast(string) data; }(); 54 validate(result); 55 return result; 56 } catch (Exception e) { 57 } 58 59 return invalidFile; 60 } 61 62 struct Table(int columnsNr) { 63 import std.format : FormatSpec; 64 65 alias Row = string[columnsNr]; 66 67 Row heading_; 68 Row[] rows; 69 ulong[columnsNr] columnWidth; 70 71 this(const Row heading) @safe { 72 this.heading = heading; 73 updateColumns(heading); 74 } 75 76 bool empty() @safe pure nothrow const @nogc { 77 return rows.length == 0; 78 } 79 80 void heading(const Row r) @safe { 81 heading_ = r; 82 updateColumns(r); 83 } 84 85 void put(const Row r) @safe { 86 rows ~= r; 87 updateColumns(r); 88 } 89 90 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const { 91 import std.ascii : newline; 92 import std.range : enumerate, repeat; 93 import std.format : formattedWrite; 94 import std.range.primitives : put; 95 96 immutable sep = "|"; 97 immutable lhs_sep = "| "; 98 immutable mid_sep = " | "; 99 immutable rhs_sep = " |"; 100 101 void printRow(const ref Row r) { 102 foreach (const r_; r[].enumerate) { 103 if (r_.index == 0) 104 put(w, lhs_sep); 105 else 106 put(w, mid_sep); 107 formattedWrite(w, "%-*s", columnWidth[r_.index], r_.value); 108 } 109 put(w, rhs_sep); 110 put(w, newline); 111 } 112 113 printRow(heading_); 114 115 immutable dash = "-"; 116 foreach (len; columnWidth) { 117 put(w, sep); 118 put(w, repeat(dash, len + 2)); 119 } 120 put(w, sep); 121 put(w, newline); 122 123 foreach (const ref r; rows) { 124 printRow(r); 125 } 126 } 127 128 private void updateColumns(const ref Row r) @safe { 129 import std.algorithm : filter, count, map; 130 import std.range : enumerate; 131 import std.utf : byCodeUnit; 132 import std.typecons : tuple; 133 134 foreach (a; r[].enumerate 135 .map!(a => tuple(a.index, a.value.byCodeUnit.count)) 136 .filter!(a => a[1] > columnWidth[a[0]])) { 137 columnWidth[a[0]] = a[1]; 138 } 139 } 140 } 141 142 string statusToString(Mutation.Status status) @trusted { 143 import std.conv : to; 144 145 return to!string(status); 146 } 147 148 string statusToString(ulong status) @trusted { 149 import std.conv : to; 150 151 return statusToString(status.to!(Mutation.Status)); 152 } 153 154 string kindToString(Mutation.Kind kind) @trusted { 155 import std.conv : to; 156 157 return to!string(kind); 158 } 159 160 string kindToString(long kind) @trusted { 161 import std.conv : to; 162 163 return kindToString(kind.to!(Mutation.Kind)); 164 }