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-plugin_mutate_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, SafeInput; 22 import dextool.plugin.mutate.backend.type : Mutation, Offset; 23 24 import dextool.plugin.mutate.backend.report.utility : MakeMutationTextResult, 25 makeMutationText, window, windowSize, reportMutationSubtypeStats, 26 reportStatistics, Table; 27 import dextool.plugin.mutate.backend.report.type : SimpleWriter, ReportEvent; 28 29 @safe: 30 31 /** Report mutations in a format easily imported to Excel like software. 32 */ 33 final class ReportCSV : ReportEvent { 34 import std.conv : to; 35 import std.format : format, FormatSpec; 36 import dextool.plugin.mutate.backend.utility; 37 38 const Mutation.Kind[] kinds; 39 const ReportLevel report_level; 40 FilesysIO fio; 41 42 alias Writer = void function(const(char)[]); 43 Writer writer = (const(char)[] s) { import std.stdio : write; 44 45 write(s); }; 46 47 this(Mutation.Kind[] kinds, ReportLevel report_level, FilesysIO fio) { 48 this.kinds = kinds; 49 this.report_level = report_level; 50 this.fio = fio; 51 } 52 53 override void mutationKindEvent(const MutationKind[] kind_) { 54 writeCSV(writer, "ID", "Kind", "Description", "Location", "Comment"); 55 } 56 57 override void locationStartEvent() { 58 } 59 60 override void locationEvent(const ref IterateMutantRow r) @trusted { 61 import std.conv : to; 62 63 void report() { 64 MakeMutationTextResult mut_txt; 65 AbsolutePath abs_path; 66 try { 67 abs_path = AbsolutePath(FileName(r.file), DirName(fio.getOutputDir)); 68 mut_txt = makeMutationText(fio.makeInput(abs_path), 69 r.mutationPoint.offset, r.mutation.kind); 70 } 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 } 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 formattedWrite(w, `"%s"`, a); 127 } 128 } else { 129 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 }