1 // SemiTwist D Tools
2 // Samples: semitwist.cmd sample
3 // Written in the D programming language.
4 
5 /++
6 Author:
7 $(WEB www.semitwist.com, Nick Sabalausky)
8 
9 This has been tested to work with DMD 2.058 through 2.062
10 +/
11 
12 module semitwist.apps.samples.cmdSample.main;
13 
14 import semitwist.cmd.all;
15 
16 void showSectionHeader(string str)
17 {
18 	cmd.echo();
19 	cmd.echo("--------", str, "--------");
20 }
21 
22 //TODO: Find those functions in tango to read and maybe write a file in one line.
23 //      And add the import to semitwist.cmd.all, and add samples for it here.
24 //   -> tango.io.device.File: string blah = cast(string)File.get("blah.txt");
25 
26 void main(string[] args)
27 {
28 	// ----- semitwist.cmd: cmd.pause -----
29 	// - Prompt and wait for the user to press Enter
30 	cmd.pause();
31 
32 	// ----- semitwist.cmd: cmd.echo -----
33 	// - Script-style alternative to Stdout, largely here for completeness
34 	// - May be extended later to support a limited form of
35 	//   "blah $myvar blah"-style string expansion.
36 	// - Note that a newline is automatically appended,
37 	//   which is useful for shell-script-style apps.
38 	showSectionHeader("semitwist.cmd: cmd.echo");
39 	
40 	cmd.echo("Hello");
41 	cmd.echo("This is on a separate line");
42 	cmd.echo("Multiple mixed-types:"c, 2, "+"w, 3.1, "="d, 5.1);
43 	cmd.echo("No worry about forgetting spaces:");
44 	cmd.echo(32, 64, 128, 256);
45 	class Foo {
46 		override string toString() {
47 			return "Any type that Stdout can handle is ok.";
48 		}
49 	}
50 	cmd.echo(new Foo(), "See?");
51 	write("Of course, ");
52 	writefln("ordinary writeln %s available too.", "is");
53 
54 	cmd.pause();
55 
56 	// ----- semitwist.util.text: sformat/sformatln -----
57 	// - Wraps tango's Layout seamlessly for char, wchar and dchar.
58 	// - Note that you don't need to manually instantiate it.
59 	// - Using D's array-method calling syntax is, of course, optional.
60 	//   (But I like using it.)
61 	showSectionHeader("semitwist.util.text: sformat/sformatln");
62 
63 	auto myStr8 = "Hello %s".format("Joe");
64 	cmd.echo(myStr8);
65 	auto myStr16 = "This %s wstr ends in a newline, %s"w.formatln("happy", "whee");
66 	cmd.echo(myStr16);
67 	cmd.echo("See? There was an extra newline up there.");
68 	
69 	cmd.pause();
70 
71 	// ----- semitwist.util.mixins: trace -----
72 	// - Useful for debugging.
73 	// - Outputs file and line information
74 	showSectionHeader("semitwist.util.mixins: trace");
75 
76 	trace();
77 	trace();
78 	trace("======== this is easy to spot ========");
79 	
80 	cmd.pause();
81 
82 	// ----- semitwist.util.mixins: traceVal -----
83 	// - Useful for debugging.
84 	// - DRY way to output both an expression and the expression's value
85 	showSectionHeader("semitwist.util.mixins: traceVal");
86 
87 	double myDouble = 5.55;
88 	int myInt = 7;
89 	mixin(traceVal!("myDouble", "myInt   ", "   myInt", "4*7"));
90 	mixin(traceVal!(`"Any expression %s".format("is ok")`));
91 	
92 	cmd.pause();
93 
94 	// ----- semitwist.cmd: cmd.exec -----
95 	// - Easy tango.sys.Process wrapper for running apps
96 	//
97 	// We'll test this with two small sample apps:
98 	//   - showargs: Lists the args passed into it
99 	//   - seterrorlevel: Sets the error level to a desired value
100 	/+showSectionHeader("semitwist.cmd: cmd.exec");
101 
102 	cmd.exec("semitwist-showargs Hello from D!"); // Three args
103 	cmd.exec(`semitwist-showargs "Hello from D!"`); // One arg with spaces
104 	cmd.exec("semitwist-showargs", ["arg 1"[], "arg 2"]); // Two args, each with spaces
105 	int errLevel;
106 	mixin(traceVal!("errLevel"));
107 	cmd.exec("seterrorlevel 42", errLevel);
108 	mixin(traceVal!("errLevel"));
109 	
110 	cmd.pause();+/
111 
112 	// ----- semitwist.cmd: cmd.echoing -----
113 	// - Determines whether an exec'd program's stdout/stderr are actually
114 	//   sent to this program's stdout/stderr or are hidden.
115 	// - Not to be confused with the functionality of Win's "echo on"/"echo off"
116 	//
117 	// We'll test this with a small sample app:
118 	//   - myecho: Like ordinary echo, but needed on Win because Win's echo
119 	//             is not an actual executable and therefore can't be launched
120 	//             by the tango.sys.Process used by exec.
121 	/+showSectionHeader("semitwist.cmd: cmd.echoing");
122 
123 	cmd.exec("semitwist-echo You can see this");
124 	cmd.echoing = false;
125 	cmd.exec("semitwist-echo You cannot see this");
126 	cmd.echoing = true;
127 	cmd.exec("semitwist-echo You can see this again");
128 	
129 	cmd.pause();+/
130 
131 	// ----- semitwist.cmd: cmd.prompt -----
132 	// - Easy way to prompt for information interactively
133 	showSectionHeader("semitwist.cmd: cmd.prompt");
134 
135 	string input;
136 	input = cmd.prompt("Type some stuff: ");
137 	cmd.echo("You entered:", input);
138 
139 	// Easy prompt-with-validation
140 	string promptMsg = "Do you want 'coffee' or 'tea'? ";
141 	string failureMsg = "Please enter 'coffee' or 'tea', not '{0}'";
142 	bool accept(string input)
143 	{
144 		return ["coffee", "tea"].contains(toLower(input));
145 	}
146 	// This will *not* return until the user enters a valid choice,
147 	// so we don't need to do any more validation.
148 	input = cmd.prompt(promptMsg, &accept, failureMsg);
149 	cmd.echo("Tough luck! No", input, "for you!");
150 /+
151 	// Single-char prompts
152 	auto letter = cmd.promptChar("Enter any letter: ");
153 	cmd.echo("You entered:", letter);
154 	// Validation supported on single-char prompts, too.
155 	promptMsg = "Do you like D (y/n)? ";
156 	failureMsg = "Please enter 'y' or 'n', not '{0}'";
157 	bool acceptChar(char input)
158 	{
159 		return "YNyn".contains(input);
160 	}
161 	letter = cmd.promptChar(promptMsg, &acceptChar, failureMsg);
162 	cmd.echo("Yy".contains(letter)? "Yay!" : "Dang...");
163 +/
164 
165 	cmd.pause();
166 
167 	// ----- semitwist.cmd: cmd.dir -----
168 	// - Get/Set cmd's working directory
169 	// - Note that setting dir does NOT affect the app's working directory,
170 	//   just the directory the cmd object operates on.
171 /+	showSectionHeader("semitwist.cmd: cmd.dir");
172 
173 	cmd.echo("cmd is in", cmd.dir);          // Starts in the app's working dir
174 	cmd.dir.folder("myNewDir").create;       // dir exposes Tango's VfsFolder
175 	cmd.dir = "myNewDir";                    // Enter newly created directory
176 	cmd.dir = "..";                          // Back up
177 	cmd.dir = cmd.dir.folder("new2").create; // Create/enter new dir in one line
178 	  // (Note that cmd.dir can be assigned either string or Tango's VfsFolder)
179 	
180 	// Create/open/write/close a new file in this new "new2" directory.
181 	auto filename = "file.txt";
182 	auto fout = cmd.dir.file(filename).create.output;
183 	fout.write("Content of {0}".sformat(filename)); // Easy templated content!
184 	fout.close;
185 	
186 	// Look ma! Independent simultaneous cmd interfaces!
187 	auto cmd2 = new Cmd();
188 	// cmd2's dir is unaffected by cmd's dir
189 	mixin(traceVal!("cmd.dir", "cmd2.dir")); // Note the usefullness of traceVal
190 	
191 	// Changing dir doesn't change the app's working dir,
192 	// but we can still change it if we want to.
193 	// Remember that cmd is still in the newly-created "new2",
194 	// so let's go there:
195 	mixin(traceVal!("Environment.cwd")); // Original working dir
196 	Environment.cwd = cmd.dir.toString;
197 	mixin(traceVal!("Environment.cwd")); // New working dir
198 	
199 	// Now a newly created Cmd is in "new2",
200 	// because we changed the app's working directory.
201 	// (But ordinarily, you wouldn't need to do this. It's just to illustrate.)
202 	auto cmd3 = new Cmd();
203 	mixin(traceVal!("cmd3.dir"));
204 	
205 	// Let's undo all the directory changing now:
206 	cmd.dir = "..";
207 	// (note we never changed cmd2's dir)
208 	cmd3.dir = "..";
209 	Environment.cwd = cmd.dir.toString;
210 	// Now all the working dirs are back to their original state.
211 	// We also could have done it like this:
212 	//
213 	// auto saveOriginalDir = cmd.dir;
214 	//   ...do all our messing around...
215 	// cmd.dir = saveOriginalDir;
216 	// cmd3.dir = saveOriginalDir;
217 	// Environment.cwd = saveOriginalDir.toString;
218 
219 	// ----- tango.io.vfs.FileFolder -----
220 	// - More explanation and examples of this are available at:
221 	//   http://www.dsource.org/projects/tango/wiki/ChapterVFS
222 	showSectionHeader("tango.io.vfs.FileFolder");
223 
224 	cmd.echo("Current '.' dir is "~cmd.dir.toString);
225 	cmd.echo("cmd.dir contains:");
226 	foreach(VfsFolder folder; cmd.dir)
227 	{
228 		// Remember, in D, mixins can generate multiple statements
229 		mixin(traceVal!("folder", "folder.name"));
230 	}
231 
232 	auto entireParentTree = cmd.dir.folder("..").open.tree;
233 	auto entireParentDir = cmd.dir.folder("..").open.self;
234 	
235 	cmd.echo("Total bytes in '..' dir: %s".format(entireParentDir.bytes));
236 	cmd.echo("Total bytes in entire '..' tree: %s".format(entireParentTree.bytes));
237 	
238 	cmd.echo("Total num folders in '..' dir: %s".format(entireParentDir.folders));
239 	foreach(VfsFolder folder; cmd.dir.folder("..").open)
240 	{
241 		cmd.echo(" -", folder);
242 		cmd.echo("    (", (new FilePath(normalize(folder.toString))).native, ")");
243 		cmd.echo();
244 	}
245 	
246 	cmd.echo("Total num folders in entire '..' tree: %s".format(entireParentTree.folders));
247 	foreach(VfsFolder folder; cmd.dir)
248 		cmd.echo(" -"~folder.toString);
249 	
250 	cmd.echo("Total num files in '..' dir: %s".format(entireParentDir.files));
251 	foreach(VfsFile file; entireParentDir.catalog)
252 		cmd.echo(" -"~file.toString);
253 	
254 	cmd.echo("Total num files in entire '..' tree: %s".format(entireParentTree.files));
255 	foreach(VfsFile file; entireParentTree.catalog)
256 		cmd.echo(" -"~file.toString);
257 	
258 	cmd.echo("Total num *.txt files in entire '..' tree: %s".format(entireParentTree.catalog("*.txt").files));
259 	foreach(VfsFile file; entireParentTree.catalog("*.txt"))
260 		cmd.echo(" -"~file.toString);
261 		
262 	cmd.pause();
263 
264 +/
265 	//TODO: Add deferAssert stuff
266 }