1 /**
2 Copyright: Copyright (c) 2017, 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 module dextool.plugin.mutate.backend.interface_;
11 
12 import std.exception : collectException;
13 import std.stdio : File;
14 import logger = std.experimental.logger;
15 
16 public import blob_model : Blob;
17 
18 import dextool.type : AbsolutePath;
19 
20 @safe:
21 
22 /** Validate source code locations for analyze and mutation.
23  */
24 interface ValidateLoc {
25     /// Returns: the root directory that files to be mutated must reside inside
26     AbsolutePath getOutputDir() nothrow;
27 
28     /// Returns: if a path should be analyzed for mutation points.
29     bool shouldAnalyze(AbsolutePath p);
30 
31     /// ditto
32     bool shouldAnalyze(string p);
33 
34     /// Returns: if a mutant are allowed to be written to this path.
35     bool shouldMutate(AbsolutePath p);
36 
37     /// Duplicate the instance,
38     ValidateLoc dup();
39 }
40 
41 /** Filesystem I/O from the backend.
42  *
43  * TODO: rename outputDir to workdir. It is the terminology used in git.
44  *
45  * The implementation of the interface shall:
46  *  ensure all SafeOutput objects are inside the _output directory_.
47  *
48  * The design is intended to create a clear distinction between output and
49  * input. It is to make it easier to do code review and reason about where
50  * filesystem output is created.
51  */
52 interface FilesysIO {
53     import dextool.type : Path;
54     import std.stdio : File;
55 
56     // these are here so backend do not need to import std.stdio which makes it
57     // easier to review.
58     File getDevNull();
59     File getStdin();
60 
61     /// Convert a path to be relative to the root of the filesystem.
62     Path toRelativeRoot(Path p);
63 
64     /// File output is restricted to this directory
65     AbsolutePath getOutputDir() nothrow;
66 
67     ///
68     SafeOutput makeOutput(AbsolutePath p);
69 
70     ///
71     Blob makeInput(AbsolutePath p);
72 
73     /// Duplicate the instance,
74     FilesysIO dup();
75 
76 protected:
77     void putFile(AbsolutePath fname, const(ubyte)[] data);
78 }
79 
80 struct SafeOutput {
81     import std.array : Appender;
82 
83     private AbsolutePath fname;
84     private FilesysIO fsys;
85     private Appender!(ubyte[]) buf;
86     private bool is_open;
87 
88     @disable this(this);
89 
90     this(AbsolutePath fname, FilesysIO fsys) {
91         this.fname = fname;
92         this.fsys = fsys;
93         this.is_open = true;
94     }
95 
96     ~this() {
97         close();
98     }
99 
100     // trusted: the data is copied therefore it is safe to cast away const.
101     void write(T)(inout T data) @trusted if (!is(T == ubyte[])) {
102         buf.put(cast(ubyte[]) data);
103     }
104 
105     void write(T)(inout T data) if (is(T == ubyte[])) {
106         buf.put(data);
107     }
108 
109     void write(Blob b) {
110         buf.put(b.content);
111     }
112 
113     void close() {
114         if (is_open) {
115             fsys.putFile(fname, buf.data);
116             buf.clear;
117         }
118         is_open = false;
119     }
120 }