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     /// Convert a path to an absolute path relative to the root.
65     AbsolutePath toAbsoluteRoot(Path p);
66 
67     /// File output is restricted to this directory
68     AbsolutePath getOutputDir() nothrow;
69 
70     ///
71     SafeOutput makeOutput(AbsolutePath p);
72 
73     ///
74     Blob makeInput(AbsolutePath p);
75 
76     /// Duplicate the instance,
77     FilesysIO dup();
78 
79 protected:
80     void putFile(AbsolutePath fname, const(ubyte)[] data);
81 }
82 
83 struct SafeOutput {
84     import std.array : Appender;
85 
86     private AbsolutePath fname;
87     private FilesysIO fsys;
88     private Appender!(ubyte[]) buf;
89     private bool is_open;
90 
91     @disable this(this);
92 
93     this(AbsolutePath fname, FilesysIO fsys) {
94         this.fname = fname;
95         this.fsys = fsys;
96         this.is_open = true;
97     }
98 
99     ~this() {
100         close();
101     }
102 
103     // trusted: the data is copied therefore it is safe to cast away const.
104     void write(T)(inout T data) @trusted if (!is(T == ubyte[])) {
105         buf.put(cast(ubyte[]) data);
106     }
107 
108     void write(T)(inout T data) if (is(T == ubyte[])) {
109         buf.put(data);
110     }
111 
112     void write(Blob b) {
113         buf.put(b.content);
114     }
115 
116     void close() {
117         if (is_open) {
118             fsys.putFile(fname, buf.data);
119             buf.clear;
120         }
121         is_open = false;
122     }
123 }