From 0eb6bfe700856fb8731e9a4311b6d036c30606d5 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Wed, 4 Jun 2014 16:42:13 -0400 Subject: [PATCH] Add chapter on command-line arguments --- README.md | 1 + examples/staging/arg/args.rs | 11 ++++ examples/staging/arg/getopts/echo.rs | 64 +++++++++++++++++++ examples/staging/arg/getopts/input.md | 70 +++++++++++++++++++++ examples/staging/arg/getopts/test.sh | 20 ++++++ examples/staging/arg/getopts/testopt.rs | 38 +++++++++++ examples/staging/arg/input.md | 10 +++ examples/staging/arg/matching/input.md | 30 +++++++++ examples/staging/arg/matching/match_args.rs | 64 +++++++++++++++++++ examples/structure.json | 5 +- 10 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 examples/staging/arg/args.rs create mode 100644 examples/staging/arg/getopts/echo.rs create mode 100644 examples/staging/arg/getopts/input.md create mode 100644 examples/staging/arg/getopts/test.sh create mode 100644 examples/staging/arg/getopts/testopt.rs create mode 100644 examples/staging/arg/input.md create mode 100644 examples/staging/arg/matching/input.md create mode 100644 examples/staging/arg/matching/match_args.rs diff --git a/README.md b/README.md index 2ff5bd7450..e616dc7caa 100644 --- a/README.md +++ b/README.md @@ -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 + diff --git a/examples/staging/arg/args.rs b/examples/staging/arg/args.rs new file mode 100644 index 0000000000..448b040891 --- /dev/null +++ b/examples/staging/arg/args.rs @@ -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()); +} diff --git a/examples/staging/arg/getopts/echo.rs b/examples/staging/arg/getopts/echo.rs new file mode 100644 index 0000000000..f47a9eadb2 --- /dev/null +++ b/examples/staging/arg/getopts/echo.rs @@ -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(); + } +} diff --git a/examples/staging/arg/getopts/input.md b/examples/staging/arg/getopts/input.md new file mode 100644 index 0000000000..53a14d41ae --- /dev/null +++ b/examples/staging/arg/getopts/input.md @@ -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: [-] +``` diff --git a/examples/staging/arg/getopts/test.sh b/examples/staging/arg/getopts/test.sh new file mode 100644 index 0000000000..9beb289f1d --- /dev/null +++ b/examples/staging/arg/getopts/test.sh @@ -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 -' diff --git a/examples/staging/arg/getopts/testopt.rs b/examples/staging/arg/getopts/testopt.rs new file mode 100644 index 0000000000..da488d249b --- /dev/null +++ b/examples/staging/arg/getopts/testopt.rs @@ -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); + } +} diff --git a/examples/staging/arg/input.md b/examples/staging/arg/input.md new file mode 100644 index 0000000000..8a2f0a2c06 --- /dev/null +++ b/examples/staging/arg/input.md @@ -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]. +``` diff --git a/examples/staging/arg/matching/input.md b/examples/staging/arg/matching/input.md new file mode 100644 index 0000000000..0d97b55a57 --- /dev/null +++ b/examples/staging/arg/matching/input.md @@ -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 + Check whether given string is the answer. +match_args {increase|decrease} + Increase or decrease given integer by one. +$ ./match_args do 42 +error: invalid command +usage: +match_args + Check whether given string is the answer. +match_args {increase|decrease} + Increase or decrease given integer by one. +$ ./match_args increase 42 +43 +``` + + +For implementing more complicated, unix-like command line interfaces see the `getopts` example. + diff --git a/examples/staging/arg/matching/match_args.rs b/examples/staging/arg/matching/match_args.rs new file mode 100644 index 0000000000..037f1cab9f --- /dev/null +++ b/examples/staging/arg/matching/match_args.rs @@ -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 + Check whether given string is the answer. +match_args {{increase|decrease}} + 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(); + } + } +} diff --git a/examples/structure.json b/examples/structure.json index fda9f9e800..bf72533aa1 100644 --- a/examples/structure.json +++ b/examples/structure.json @@ -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 }, @@ -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 },