Skip to content

Commit

Permalink
Add chapter on command-line arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
vks committed Dec 11, 2014
1 parent 4634260 commit 0eb6bfe
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,4 @@ See LICENSE-APACHE and LICENSE-MIT for more details.
[hello-folder]: examples/hello
[hello-rs]: examples/hello/hello.rs
[hello-md]: examples/hello/input.md

11 changes: 11 additions & 0 deletions examples/staging/arg/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use std::os;

fn main() {
let args = os::args();
// The first argument is the path that was used to call the program.
println!("My path is {}.", args[0]);
// The rest of the arguments are the passed command line parameters.
// Call the program like this:
// $ ./args arg1 arg2
println!("I got {} arguments: {}.", args.len() - 1, args.tail());
}
64 changes: 64 additions & 0 deletions examples/staging/arg/getopts/echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
extern crate getopts;

use std::os;
use std::io::{print, println};
use std::io::stdio;

static VERSION: &'static str = "1.0.0";

fn main() {
let args = os::args();
let ref program = args[0];

// Set possible flags.
// The first argument to `optflag` is the short flag name.
// The second argument is the long flag name.
// The third argument is the help text.
let opts = [
getopts::optflag("n", "", "do not output the trailing newline"),
getopts::optflag("h", "help", "display this help and exit"),
getopts::optflag("V", "version",
"output version information and exit"),
];

let matches = match getopts::getopts(args.tail(), &opts) {
Ok(m) => m,
Err(f) => {
println!("{}", f);
os::set_exit_status(1);
return;
// The exit code is 0 (success) by default.
// Any exit code other than 0 indicates failure.
}
};

if matches.opt_present("help") {
//^ We could as well have used the short name: "h"
println!("echo {} - display a line of text", VERSION);
println!("");
println!("Usage:");
println!(" {} [SHORT-OPTION]... [STRING]...", program);
println!(" {} LONG-OPTION", program);
println!("");
println(getopts::usage("Echo the STRING(s) to standard output.", &opts)
.as_slice());
return;
}

if matches.opt_present("version") {
println!("echo version: {}", VERSION);
return;
}

if !matches.free.is_empty() {
//^ `matches.free` contains all the arguments that are not options.
let string = matches.free.connect(" ");
print(string.as_slice());
}

if !matches.opt_present("n") {
println!("")
} else {
stdio::flush();
}
}
70 changes: 70 additions & 0 deletions examples/staging/arg/getopts/input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
To build unix-style command line interfaces, you can use the [getopts](http://doc.rust-lang.org/getopts/index.html) crate.

Here is a simple implementation of the `echo` unix program:

{echo.play}

```
$ ./echo -h
echo 1.0.0 - display a line of text
Usage:
./echo [SHORT-OPTION]... [STRING]...
./echo LONG-OPTION
Echo the STRING(s) to standard output.
Options:
-n do not output the trailing newline
-h --help display this help and exit
-V --version output version information and exit
$ ./echo --version
echo version: 1.0.0
$ ./echo Hello, World!
Hello, World!
```

This is a simplified version of the `echo` implementation by
[uutils](/~https://github.com/uutils/coreutils).


It is also possible to use *options* instead of *flags*, such that values can
be passed to the program:

{testopt.rs}

Here are some examples how the program behaves given different combinations of
arguments:

```
$ ./testopt
a=false, b=false, c=""
$ ./testopt -a -b
a=true, b=true, c=""
$ ./testopt -ab
a=true, b=true, c=""
$ ./testopt -c
Argument to option 'c' missing.
$ ./testopt -c value
a=false, b=false, c="value"
$ ./testopt -c=value
a=false, b=false, c="=value"
$ ./testopt -cvalue
a=false, b=false, c="value"
$ ./testopt arg
a=false, b=false, c=""
free arguments: [arg]
$ ./testopt -a arg
a=true, b=false, c=""
free arguments: [arg]
$ ./testopt -c value arg
a=false, b=false, c="value"
free arguments: [arg]
$ ./testopt -a -- -b
a=true, b=false, c=""
free arguments: [-b]
$ ./testopt -a -
a=true, b=false, c=""
free arguments: [-]
```
20 changes: 20 additions & 0 deletions examples/staging/arg/getopts/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/sh
# Generate the example output for input.md.

function run {
echo '$' $1
$1
}

run './testopt'
run './testopt -a -b'
run './testopt -ab'
run './testopt -c'
run './testopt -c value'
run './testopt -c=value'
run './testopt -cvalue'
run './testopt arg'
run './testopt -a arg'
run './testopt -c value arg'
run './testopt -a -- -b'
run './testopt -a -'
38 changes: 38 additions & 0 deletions examples/staging/arg/getopts/testopt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
extern crate getopts;

use std::os;

fn main() {
let args = os::args();
let opts = [
getopts::optflag("a", "long_a", ""),
getopts::optflag("b", "long_b", ""),
getopts::optopt("c", "long_c", "", "VALUE"),
//^ Use `optflagopt` if the argument should be optional.
// Use `reqopt` if the option is required.
// Use `optmulti`, `optflagmulti` if options can occur multiple times.
];

let matches = match getopts::getopts(args.tail(), &opts) {
Ok(m) => m,
Err(f) => {
println!("{}", f);
os::set_exit_status(1);
return;
}
};
let a = if matches.opt_present("a") {true} else {false};
let b = if matches.opt_present("b") {true} else {false};
let c = match matches.opt_str("c") {
Some(s) => s,
None => String::from_str(""),
};
//^ Use `matches.opt_default` if you need a default (`opflagopt`).
// Use `matches.opt_count` if you need to count how many were matched
// (`*multi`).

println!("a={}, b={}, c=\"{}\"", a, b, c);
if !matches.free.is_empty() {
println!("free arguments: {}", matches.free);
}
}
10 changes: 10 additions & 0 deletions examples/staging/arg/input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
The command line arguments can be accessed using `std::os::args`, which returns
a [vector](http://static.rust-lang.org/doc/master/std/vec/index.html) of strings:

{args.play}

```
$ ./args 1 2 3
My path is ./args.
I got 3 arguments: [1, 2, 3].
```
30 changes: 30 additions & 0 deletions examples/staging/arg/matching/input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Matching can be used to parse simple arguments:

{match_args.play}

```
$ ./match_args Rust
This is not the answer.
$ ./match_args 42
This is the answer!
$ ./match_args do something
error: second argument not an integer
usage:
match_args <string>
Check whether given string is the answer.
match_args {increase|decrease} <integer>
Increase or decrease given integer by one.
$ ./match_args do 42
error: invalid command
usage:
match_args <string>
Check whether given string is the answer.
match_args {increase|decrease} <integer>
Increase or decrease given integer by one.
$ ./match_args increase 42
43
```

<!-- TODO link to the getopts example, after moving this out of the staging area -->
For implementing more complicated, unix-like command line interfaces see the `getopts` example.

64 changes: 64 additions & 0 deletions examples/staging/arg/matching/match_args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::os;

fn increase(number: int) {
println!("{}", number + 1);
}

fn decrease(number: int) {
println!("{}", number - 1);
}

fn help() {
println!("usage:
match_args <string>
Check whether given string is the answer.
match_args {{increase|decrease}} <integer>
Increase or decrease given integer by one.");
}

fn main() {
let args = os::args();

match args.as_slice() {
// no arguments passed
[ref name] => {
println!("My name is '{}'. Try passing some arguments!", name);
},
// one argument passed
[_, ref string] => {
if string.as_slice() == "42" {
println!("This is the answer!");
} else {
println!("This is not the answer.");
}
},
// one command and one argument passed
[_, ref cmd, ref num] => {
// parse the number
let number: int = match from_str(num.as_slice()) {
Some(n) => {
n
},
None => {
println!("error: second argument not an integer");
help();
return;
},
};
// parse the command
match cmd.as_slice() {
"increase" => increase(number),
"decrease" => decrease(number),
_ => {
println!("error: invalid command");
help();
},
}
},
// all the other cases
_ => {
// show a help message
help();
}
}
}
5 changes: 4 additions & 1 deletion examples/structure.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@
] },
{ "id": "ffi", "title": "Foreign Function Interface", "children": null },
{ "id": "macros", "title": "macro_rules!", "children": null },
{ "id": "arg", "title": "Program arguments", "children": [
{ "id": "matching", "title": "Argument parsing", "children": null },
{ "id": "getopts", "title": "`getopts`", "children": null }
] },
{ "id": "rand", "title": "Random", "children": null },
{ "id": "simd", "title": "SIMD", "children": null },
{ "id": "test", "title": "Testing", "children": null },
Expand All @@ -124,7 +128,6 @@
] }
] },
{ "id": "todo", "title": "TODO", "children": [
{ "id": "arg", "title": "Program arguments", "children": null },
{ "id": "assert", "title": "assert! and debug_assert!", "children": null },
{ "id": "green", "title": "Green threads", "children": null },
{ "id": "log", "title": "Logging", "children": null },
Expand Down

0 comments on commit 0eb6bfe

Please sign in to comment.