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