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 #SPC-report_as_csv 11 */ 12 module dextool.plugin.mutate.backend.report.csv; 13 14 import std.exception : collectException; 15 import logger = std.experimental.logger; 16 import std.path : buildPath; 17 18 import dextool.type; 19 20 import dextool.plugin.mutate.type : MutationKind, ReportKind, ReportLevel; 21 import dextool.plugin.mutate.backend.database : Database, IterateMutantRow; 22 import dextool.plugin.mutate.backend.interface_ : FilesysIO; 23 import dextool.plugin.mutate.backend.type : Mutation, Offset; 24 25 import dextool.plugin.mutate.backend.generate_mutant : MakeMutationTextResult, makeMutationText; 26 import dextool.plugin.mutate.backend.report.type : SimpleWriter, ReportEvent; 27 import dextool.plugin.mutate.backend.report.utility : window, windowSize; 28 import dextool.plugin.mutate.backend.report.analyzers : reportMutationSubtypeStats, 29 reportStatistics, Table; 30 31 @safe: 32 33 /** Report mutations in a format easily imported to Excel like software. 34 */ 35 final class ReportCSV : ReportEvent { 36 import std.conv : to; 37 import std.format : format, FormatSpec; 38 import dextool.plugin.mutate.backend.utility; 39 40 const Mutation.Kind[] kinds; 41 const ReportLevel report_level; 42 FilesysIO fio; 43 44 alias Writer = void function(const(char)[]); 45 Writer writer = (const(char)[] s) { import std.stdio : write; 46 47 write(s); }; 48 49 this(Mutation.Kind[] kinds, ReportLevel report_level, FilesysIO fio) { 50 this.kinds = kinds; 51 this.report_level = report_level; 52 this.fio = fio; 53 } 54 55 override void mutationKindEvent(const MutationKind[] kind_) { 56 writeCSV(writer, "ID", "Kind", "Description", "Location", "Comment"); 57 } 58 59 override void locationStartEvent(ref Database db) { 60 } 61 62 override void locationEvent(ref Database db, const ref IterateMutantRow r) @trusted { 63 import std.conv : to; 64 65 void report() { 66 MakeMutationTextResult mut_txt; 67 AbsolutePath abs_path; 68 try { 69 abs_path = AbsolutePath(buildPath(fio.getOutputDir, r.file.Path)); 70 mut_txt = makeMutationText(fio.makeInput(abs_path), 71 r.mutationPoint.offset, r.mutation.kind, r.lang); 72 } catch (Exception e) { 73 logger.warning(e.msg); 74 } 75 76 immutable textualDescriptionLen = 255; 77 auto desc = format(`'%s' to '%s'`, toField(mut_txt.original, 78 textualDescriptionLen), toField(mut_txt.mutation, textualDescriptionLen)); 79 auto loc = format("%s:%s:%s", r.file, r.sloc.line, r.sloc.column); 80 writer.writeCSV(r.id, r.mutation.kind.toUser, desc, loc, ""); 81 } 82 83 try { 84 final switch (report_level) { 85 case ReportLevel.summary: 86 break; 87 case ReportLevel.alive: 88 if (r.mutation.status == Mutation.Status.alive) { 89 report(); 90 } 91 break; 92 case ReportLevel.all: 93 report(); 94 break; 95 } 96 } catch (Exception e) { 97 logger.trace(e.msg).collectException; 98 } 99 } 100 101 override void locationEndEvent() { 102 } 103 104 override void locationStatEvent() { 105 } 106 107 override void statEvent(ref Database db) { 108 } 109 } 110 111 private: 112 113 /// Write a line as CSV 114 void writeCSV(Writer, T...)(scope Writer w, auto ref T args) { 115 import std.ascii : newline; 116 import std.format : formattedWrite; 117 import std.range.primitives : put; 118 119 bool first = true; 120 foreach (a; args) { 121 if (!first) 122 put(w, ","); 123 124 static if (__traits(hasMember, a, "isNull")) { 125 if (!a.isNull) { 126 () @trusted { formattedWrite(w, `"%s"`, a); }(); 127 } 128 } else { 129 () @trusted { formattedWrite(w, `"%s"`, a); }(); 130 } 131 132 first = false; 133 } 134 135 put(w, newline); 136 } 137 138 /// Returns: conversion to a valid CSV field. 139 auto toField(T)(T r, size_t maxlen) { 140 import std.algorithm : filter; 141 import std.range : take; 142 143 return r.take(maxlen).filter!(a => a != '"'); 144 }