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