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 }