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. Since this is used for logging, don't forget to flush your output.
59 
60 Reset this to null to go back to Scriptlike's default of "echo to stdout" again.
61 
62 Note, setting this does not automatically enable echoing. You still need to
63 set either scriptlikeEcho or scriptlikeDryRun to true.
64 +/
65 void delegate(string) scriptlikeCustomEcho;
66 
67 /++
68 Output text lazily through scriptlike's echo logger.
69 Does nothing if scriptlikeEcho and scriptlikeDryRun are both false.
70 
71 The yapFunc version automatically prepends the output with the
72 name of the calling function. Ex:
73 
74 ----------------
75 void foo(int i = 42) {
76 	// Outputs:
77 	// foo: i = 42
78 	yapFunc("i = ", i);
79 }
80 ----------------
81 +/
82 void yap(T...)(lazy T args)
83 {
84 	import std.stdio;
85 	
86 	if(scriptlikeEcho || scriptlikeDryRun)
87 	{
88 		if(scriptlikeCustomEcho)
89 			scriptlikeCustomEcho(text(args));
90 		else
91 		{
92 			writeln(args);
93 			stdout.flush();
94 		}
95 	}
96 }
97 
98 ///ditto
99 void yapFunc(string funcName=__FUNCTION__, T...)(lazy T args)
100 {
101 	static assert(funcName != "");
102 	
103 	auto funcNameSimple = funcName.split(".")[$-1];
104 	yap(funcNameSimple, ": ", args);
105 }
106 
107 /// Maintained for backwards-compatibility. Will be deprecated.
108 /// Use 'yap' instead.
109 void echoCommand(lazy string msg)
110 {
111 	yap(msg);
112 }