1 // Scriptlike: Utility to aid in script-like programs.
2 // Written in the D programming language.
3 
4 /// Copyright: Copyright (C) 2014-2015 Nick Sabalausky
5 /// License:   $(LINK2 https://github.com/Abscissa/scriptlike/blob/master/LICENSE.txt, zlib/libpng)
6 /// Authors:   Nick Sabalausky
7 
8 module scriptlike.core;
9 
10 import std.conv;
11 import std.string;
12 
13 /// If true, all commands will be echoed. By default, they will be
14 /// echoed to stdout, but you can override this with scriptlikeCustomEcho.
15 bool scriptlikeEcho = false;
16 
17 /// Alias for backwards-compatibility. This will be deprecated in the future.
18 /// You should use scriptlikeEcho insetad.
19 alias scriptlikeTraceCommands = scriptlikeEcho;
20 
21 /++
22 If true, then run, tryRun, file write, file append, and all the echoable
23 commands that modify the filesystem will be echoed to stdout (regardless
24 of scriptlikeEcho) and NOT actually executed.
25 
26 Warning! This is NOT a "set it and forget it" switch. You must still take
27 care to write your script in a way that's dryrun-safe. Two things to remember:
28 
29 1. ONLY Scriptlike's functions will obey this setting. Calling Phobos
30 functions directly will BYPASS this setting.
31 
32 2. If part of your script relies on a command having ACTUALLY been run, then
33 that command will fail. You must avoid that situation or work around it.
34 For example:
35 
36 ---------------------
37 run(`date > tempfile`);
38 
39 // The following will FAIL or behave INCORRECTLY in dryrun mode:
40 auto data = cast(string)read("tempfile");
41 run("echo "~data);
42 ---------------------
43 
44 That may be an unrealistic example, but it demonstrates the problem: Normally,
45 the code above should run fine (at least on posix). But in dryrun mode,
46 "date" will not actually be run. Therefore, tempfile will neither be created
47 nor overwritten. Result: Either an exception reading a non-existent file,
48 or outdated information will be displayed.
49 
50 Scriptlike cannot anticipate or handle such situations. So it's up to you to
51 make sure your script is dryrun-safe.
52 +/
53 bool scriptlikeDryRun = false;
54 
55 /++
56 By default, scriptlikeEcho and scriptlikeDryRun echo to stdout.
57 You can override this behavior by setting scriptlikeCustomEcho to your own
58 sink delegate. Set this to null to go back to Scriptlike's default
59 of "echo to stdout" again.
60 
61 Note, setting this does not automatically enable echoing. You still need to
62 set either scriptlikeEcho or scriptlikeDryRun to true.
63 +/
64 void delegate(string) scriptlikeCustomEcho;
65 
66 /++
67 Output text lazily through scriptlike's echo logger.
68 Does nothing if scriptlikeEcho and scriptlikeDryRun are both false.
69 
70 The yapFunc version automatically prepends the output with the
71 name of the calling function. Ex:
72 
73 ----------------
74 void foo(int i = 42) {
75 	// Outputs:
76 	// foo: i = 42
77 	yapFunc("i = ", i);
78 }
79 ----------------
80 +/
81 void yap(T...)(lazy T args)
82 {
83 	import std.stdio;
84 	
85 	if(scriptlikeEcho || scriptlikeDryRun)
86 	{
87 		if(scriptlikeCustomEcho)
88 			scriptlikeCustomEcho(text(args));
89 		else
90 			writeln(args);
91 	}
92 }
93 
94 ///ditto
95 void yapFunc(string funcName=__FUNCTION__, T...)(lazy T args)
96 {
97 	static assert(funcName != "");
98 	
99 	auto funcNameSimple = funcName.split(".")[$-1];
100 	yap(funcNameSimple, ": ", args);
101 }
102 
103 /// Maintained for backwards-compatibility. Will be deprecated.
104 /// Use 'yap' instead.
105 void echoCommand(lazy string msg)
106 {
107 	yap(msg);
108 }