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 }