scriptlike ~examples
Utility to aid in script-like programs.
To use this package, run the following command in your project's root directory:
Manual usage
Put the following dependency into your project's dependences section:
Scriptlike
Utility to aid in writing script-like programs in the D Programming Language.
Officially supported compiler versions are shown in .travis.yml.
Links:
- How to Use Scriptlike in Scripts
- API Reference
- Changelog
- DUB Package
- Small article explaining the original motivations behind scriptlike
Features
- Automatic Phobos Import
- User Input Prompts
- String Interpolation
- Filepaths
- Try/As Filesystem Operations
- Script-Style Shell Commands
- Command Echoing
- Dry Run Assistance
- Fail
Automatic Phobos Import
For most typical Phobos modules. Unless you don't want to. Who needs rows and rows of standard lib imports for a mere script?
import scriptlike;
//import scriptlike.only; // In case you don't want Phobos auto-imported
void main() {
writeln("Works!");
}
See: `scriptlike
`, `scriptlike.only
`, `scriptlike.std
`
User Input Prompts
Easy prompting for and verifying command-line user input with the `interact
` module:
auto name = userInput!string("Please enter your name");
auto age = userInput!int("And your age");
if(userInput!bool("Do you want to continue?"))
{
string outputFolder = pathLocation("Where you do want to place the output?");
auto color = menu!string("What color would you like to use?", ["Blue", "Green"]);
}
auto num = require!(int, "a > 0 && a <= 10")("Enter a number from 1 to 10");
pause(); // Prompt "Press Enter to continue...";
pause("Hit Enter again, dood!!");
See: `userInput
`, `pathLocation
`, `menu
`, `require
`, `pause
`
String Interpolation
Variable (and expression) expansion inside strings:
// Output: The number 21 doubled is 42!
int num = 21;
writeln( mixin(interp!"The number ${num} doubled is ${num * 2}!") );
// Output: Empty braces output nothing.
writeln( mixin(interp!"Empty ${}braces ${}output nothing.") );
// Output: Multiple params: John Doe.
auto first = "John", last = "Doe";
writeln( mixin(interp!`Multiple params: ${first, " ", last}.`) );
See: `interp
`
Filepaths
Simple, reliable, cross-platform. No more worrying about slashes, paths-with-spaces, buildPath, normalizing, or getting paths mixed up with ordinary strings:
// This is AUTOMATICALLY kept normalized (via std.path.buildNormalizedPath)
auto dir = Path("foo/bar");
dir ~= "subdir"; // Append a subdirectory
// No worries about trailing slashes!
assert(Path("foo/bar") == Path("foo/bar/"));
assert(Path("foo/bar/") == Path("foo/bar//"));
// No worries about forward/backslashes!
assert(dir == Path("foo/bar/subdir"));
assert(dir == Path("foo\\bar\\subdir"));
// No worries about spaces!
auto file = dir.up ~ "different subdir\\Filename with spaces.txt";
assert(file == Path("foo/bar/different subdir/Filename with spaces.txt"));
writeln(file); // Path.toString() always properly escapes for current platform!
writeln(file.toRawString()); // Don't escape!
// Even file extentions are type-safe!
Ext ext = file.extension;
auto anotherFile = Path("path/to/file") ~ ext;
assert(anotherFile.baseName == Path("file.txt"));
// std.path and std.file are wrapped to offer Path/Ext support
assert(dirName(anotherFile) == Path("path/to"));
copy(anotherFile, Path("target/path/new file.txt"));
See: `Path
`, `Path.toString
`, `Path.toRawString
`, `Path.up
`, `Ext
`, `dirName
`, `copy
`, `buildNormalizedPath
`
Try/As Filesystem Operations
Less pedantic, when you don't care if there's nothing to do:
// Just MAKE SURE this exists! If it's already there, then GREAT!
tryMkdir("somedir");
assertThrown( mkdir("somedir") ); // Exception: Already exists!
tryMkdir("somedir"); // Works fine!
// Just MAKE SURE this is gone! If it's already gone, then GREAT!
tryRmdir("somedir");
assertThrown( rmdir("somedir") ); // Exception: Already gone!
tryRmdir("somedir"); // Works fine!
// Just MAKE SURE it doesn't exist. Don't bother me if it doesn't!
tryRemove("file");
// Copy if it exists, otherwise don't worry about it.
tryCopy("file", "file-copy");
// Is this a directory? If it doesn't even exist,
// then it's obviously NOT a directory.
assertThrown( isDir("foo/bar") ); // Exception: Doesn't exist!
if(existsAsDir("foo/bar")) // Works fine!
{/+ ...do stuff... +/}
// Bonus! Single function to delete files OR directories!
writeFile("file.txt", "abc");
tryMkdirRecurse("foo/bar/dir");
writeFile("foo/bar/dir/file.txt", "123");
// Delete with the same function!
removePath("file.txt"); // Calls 'remove'
removePath("foo"); // Calls 'rmdirRecurse'
tryRemovePath("file.txt"); // Also comes in try flavor!
tryRemovePath("foo");
See: `tryMkdir
`, `mkdir
`, `tryMkdirRecurse
`, `mkdir
`, `tryRmdir
`, `rmdir
`, `tryRemove
`, `tryCopy
`, `existsAsDir
`, `removePath
`, `tryRemovePath
`, `writeFile
` and more...
Script-Style Shell Commands
Invoke a command script-style: synchronously with forwarded stdout/in/err from any working directory. Or capture the output instead. Automatically throw on non-zero status code if you want.
One simple call, `run
`, to run a shell command script-style (ie, synchronously with forwarded stdout/in/err) from any working directory, and automatically throw if it fails. Or `runCollect
` to capture the output instead of displaying it. Or `tryRun
`/`tryRunCollect
` if you want to receive the status code instead of automatically throwing on non-zero.
run("dmd --help"); // Display DMD help screen
pause(); // Wait for user to hit Enter
// Automatically throws ErrorLevelException(1, "dmd --bad-flag")
assertThrown!ErrorLevelException( run("dmd --bad-flag") );
// Automatically throws ErrorLevelException(-1, "this-cmd-does-not-exist")
assertThrown!ErrorLevelException( run("this-cmd-does-not-exist") );
// Don't bail on error
int statusCode = tryRun("dmd --bad-flag");
// Collect output instead of showing it
string dmdHelp = runCollect("dmd --help");
auto isDMD_2_068_1 = dmdHelp.canFind("D Compiler v2.068.1");
// Don't bail on error
auto result = tryRunCollect("dmd --help");
if(result.status == 0 && result.output.canFind("D Compiler v2.068.1"))
writeln("Found DMD v2.068.1!");
// Use any working directory:
auto myProjectDir = Path("my/proj/dir");
auto mainFile = Path("src/main.d");
myProjectDir.run(text("dmd ", mainFile, " -O")); // mainFile is properly escaped!
// Verify it actually IS running from a different working directory:
version(Posix) enum pwd = "pwd";
else version(Windows) enum pwd = "cd";
else static assert(0);
auto output = myProjectDir.runCollect(pwd);
auto expected = getcwd() ~ myProjectDir;
assert( Path(output.strip()) == expected );
See: `run
`, `tryRun
`, `runCollect
`, `tryRunCollect
`, `pause
`, `Path
`, `getcwd
`, `ErrorLevelException
`, `assertThrown
`, `canFind
`, `text
`, `strip
`
Command Echoing
Optionally enable automatic command echoing (including shell commands, changing/creating directories and deleting/copying/moving/linking/renaming both directories and files) by setting one simple flag: `bool scriptlikeEcho
`
Echoing can be customized via `scriptlikeCustomEcho
`.
/++
Output:
--------
run: echo Hello > file.txt
mkdirRecurse: some/new/dir
copy: file.txt -> 'some/new/dir/target name.txt'
Gonna run foo() now...
foo: i = 42
--------
+/
scriptlikeEcho = true; // Enable automatic echoing
run("echo Hello > file.txt");
auto newDir = Path("some/new/dir");
mkdirRecurse(newDir.toRawString()); // Even works with non-Path overloads
copy("file.txt", newDir ~ "target name.txt");
void foo(int i = 42) {
yapFunc("i = ", i); // Evaluated lazily
}
// yap and yapFunc ONLY output when echoing is enabled
yap("Gonna run foo() now...");
foo();
See: `scriptlikeEcho
`, `yap
`, `yapFunc
`, `run
`, `Path
`, `Path.toRawString
`, `mkdirRecurse
`, `copy
`
Dry Run Assistance
Scriptlike can help you create a dry-run mode, by automatically echoing (even if `scriptlikeEcho
` is disabled) and disabling all functions that launch external commands or modify the filesystem. Just enable the `scriptlikeDryRun
` flag.
Note, if you choose to use this, you still must ensure your program logic behaves sanely in dry-run mode.
scriptlikeDryRun = true;
// When dry-run is enabled, this echoes but doesn't actually copy or invoke DMD.
copy("original.d", "app.d");
run("dmd app.d -ofbin/app");
// Works fine in dry-run, since it doesn't modify the filesystem.
bool isItThere = exists("another-file");
if(!scriptlikeDryRun)
{
// This won't work right if we're running in dry-run mode,
// since it'll be out-of-date, if it even exists at all.
auto source = read("app.d");
}
See: `scriptlikeDryRun
`, `copy
`, `run
`, `exists
`, `read
`
Fail
Single function to bail out with an error message, exception-safe.
/++
/++
Example:
--------
$ test
test: ERROR: Need two args, not 0!
$ test abc 123
test: ERROR: First arg must be 'foobar', not 'abc'!
--------
+/
import scriptlike;
void main(string[] args) {
helper(args);
}
// Throws a Fail exception on bad args:
void helper(string[] args) {
// Like std.exception.enforce, but bails with no ugly stack trace,
// and if uncaught, outputs the program name and "ERROR: "
failEnforce(args.length == 3, "Need two args, not ", args.length-1, "!");
if(args[1] != "foobar")
fail("First arg must be 'foobar', not '", args[1], "'!");
}
See: `fail
`, `failEnforce
`, `Fail
`
Disambiguating write and write
Since they're both imported by default, you may get symbol conflict errors when trying to use `scriptlike.file.wrappers.write
` (which wraps `std.file.write
`) or `std.stdio.write
`. And unfortunately, DMD issue #11847 currently makes it impossible to use a qualified name lookup for `scriptlike.file.wrappers.write
`.
Here's how to easily avoid symbol conflict errors with Scriptlike and `write
`:
// Save file
write("filename.txt", "content");
// Change line above to...
writeFile("filename.txt", "content"); // Convenience alias included in scriptlike
// Output to stdout with no newline
write("Hello world");
// Change line above to...
std.stdio.write("Hello world");
// or...
stdout.write("Hello world");
See: `scriptlike.file.wrappers.writeFile
`, `scriptlike.file.wrappers.readFile
`, `scriptlike.file.wrappers.write
`, `std.file.write
`, `std.stdio.write
`
- Registered by Nick Sabalausky
- ~examples released 9 years ago
- Abscissa/scriptlike
- github.com/abscissa/scriptlike
- zlib/libpng
- Authors:
- Dependencies:
- none
- Versions:
-
0.10.3 2019-Jul-15 0.10.2 2017-Mar-03 0.10.1 2017-Feb-26 0.10.0 2017-Feb-26 0.9.7 2017-Feb-23 - Download Stats:
-
-
6 downloads today
-
27 downloads this week
-
84 downloads this month
-
14167 downloads total
-
- Score:
- 3.3
- Short URL:
- scriptlike.dub.pm