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 }