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 }