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