1 /**
2 Copyright: Copyright (c) 2019, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module dsnapshot.process;
7 
8 import logger = std.experimental.logger;
9 import std.process;
10 public import std.process : wait;
11 public import std.typecons : Flag, Yes;
12 
13 version (unittest) {
14     import unit_threaded.assertions;
15 }
16 
17 class ProcessException : Exception {
18     int exitCode;
19 
20     this(int exitCode, string msg = null, string file = __FILE__, int line = __LINE__) @safe pure nothrow {
21         super(msg, file, line);
22         this.exitCode = exitCode;
23     }
24 }
25 
26 auto spawnProcessLog(Args...)(const(string)[] cmd_, auto ref Args args) {
27     logger.infof("%-(%s %)", cmd_);
28     return spawnProcess(cmd_, args);
29 }
30 
31 /**
32  * Params:
33  * throwOnFailure = if true throws a `ProcessException` when the exit code isn't zero.
34  */
35 auto spawnProcessLog(Flag!"throwOnFailure" throw_, Args...)(string[] cmd_, auto ref Args args) {
36     logger.infof("%-(%s %)", cmd_);
37     auto pid = spawnProcess(cmd_, args);
38     return WrapPid!(throw_)(pid);
39 }
40 
41 /** Blocks until finished.
42  */
43 auto executeLog(Args...)(const(string)[] cmd_, auto ref Args args) {
44     logger.infof("%-(%s %)", cmd_);
45     return execute(cmd_, args);
46 }
47 
48 struct WrapPid(Flag!"throwOnFailure" throw_) {
49     Pid pid;
50 
51     auto wait() {
52         auto ec = std.process.wait(pid);
53 
54         static if (throw_) {
55             if (ec != 0)
56                 throw new ProcessException(ec);
57         }
58 
59         return ec;
60     }
61 }
62 
63 @("shall throw a ProcessException when the process fails")
64 unittest {
65     spawnProcessLog!(Yes.throwOnFailure)(["false"]).wait.shouldThrow!(ProcessException);
66 }