@@ -1573,9 +1573,8 @@ detail on Getopts, but there is [some good documentation][15]
1573
1573
describing it. The short story is that Getopts generates an argument
1574
1574
parser and a help message from a vector of options (The fact that it
1575
1575
is a vector is hidden behind a struct and a set of methods). Once the
1576
- parsing is done, the parser returns a struct that records matches
1577
- for defined options, and remaining "free" arguments.
1578
- From there, we can get information about the flags, for
1576
+ parsing is done, we can decode the program arguments into a Rust
1577
+ struct. From there, we can get information about the flags, for
1579
1578
instance, whether they were passed in, and what arguments they
1580
1579
had. Here's our program with the appropriate ` extern crate `
1581
1580
statements, and the basic argument setup for Getopts:
@@ -1606,8 +1605,8 @@ fn main() {
1606
1605
print_usage(&program, opts);
1607
1606
return;
1608
1607
}
1609
- let data_path = &matches.free[0 ];
1610
- let city: &str = &matches.free[1 ];
1608
+ let data_path = &args[1 ];
1609
+ let city = &args[2 ];
1611
1610
1612
1611
// Do stuff with information
1613
1612
}
@@ -1681,8 +1680,8 @@ fn main() {
1681
1680
return;
1682
1681
}
1683
1682
1684
- let data_path = &matches.free[0 ];
1685
- let city: &str = &matches.free[1 ];
1683
+ let data_path = &args[1 ];
1684
+ let city: &str = &args[2 ];
1686
1685
1687
1686
let file = File::open(data_path).unwrap();
1688
1687
let mut rdr = csv::Reader::from_reader(file);
@@ -1793,15 +1792,13 @@ fn main() {
1793
1792
Ok(m) => { m }
1794
1793
Err(e) => { panic!(e.to_string()) }
1795
1794
};
1796
-
1797
1795
if matches.opt_present("h") {
1798
1796
print_usage(&program, opts);
1799
1797
return;
1800
1798
}
1801
1799
1802
- let data_path = &matches.free[0];
1803
- let city: &str = &matches.free[1];
1804
-
1800
+ let data_path = &args[1];
1801
+ let city = &args[2];
1805
1802
for pop in search(data_path, city) {
1806
1803
println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1807
1804
}
@@ -1879,14 +1876,14 @@ when calling `search`:
1879
1876
1880
1877
``` rust,ignore
1881
1878
...
1882
- match search(data_path, city) {
1883
- Ok(pops) => {
1884
- for pop in pops {
1885
- println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1886
- }
1879
+ match search(&data_file, &city) {
1880
+ Ok(pops) => {
1881
+ for pop in pops {
1882
+ println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1887
1883
}
1888
- Err(err) => println!("{}", err)
1889
1884
}
1885
+ Err(err) => println!("{}", err)
1886
+ }
1890
1887
...
1891
1888
```
1892
1889
@@ -1917,37 +1914,43 @@ fn print_usage(program: &str, opts: Options) {
1917
1914
println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
1918
1915
}
1919
1916
```
1920
- Of course we need to adapt the argument handling code :
1917
+ The next part is going to be only a little harder :
1921
1918
1922
1919
``` rust,ignore
1923
1920
...
1924
- let mut opts = Options::new();
1925
- opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
1926
- opts.optflag("h", "help", "Show this usage message.");
1927
- ...
1928
- let data_path = matches.opt_str("f");
1929
-
1930
- let city = if !matches.free.is_empty() {
1931
- & matches.free[0]
1932
- } else {
1933
- print_usage(&program, opts);
1934
- return ;
1935
- } ;
1936
-
1937
- match search(&data_path, city) {
1938
- Ok(pops) => {
1939
- for pop in pops {
1940
- println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1941
- }
1921
+ let mut opts = Options::new();
1922
+ opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
1923
+ opts.optflag("h", "help", "Show this usage message.");
1924
+ ...
1925
+ let file = matches.opt_str("f");
1926
+ let data_file = &file.as_ref().map(Path::new);
1927
+
1928
+ let city = if ! matches.free.is_empty() {
1929
+ &matches.free[0]
1930
+ } else {
1931
+ print_usage(&program, opts) ;
1932
+ return ;
1933
+ };
1934
+
1935
+ match search(data_file, city) {
1936
+ Ok( pops) => {
1937
+ for pop in pops {
1938
+ println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1942
1939
}
1943
- Err(err) => println!("{}", err)
1944
1940
}
1941
+ Err(err) => println!("{}", err)
1942
+ }
1945
1943
...
1946
1944
```
1947
1945
1948
- We've made the user experience a bit nicer by showing the usage message,
1949
- instead of a panic from an out-of-bounds index, when ` city ` , the
1950
- remaining free argument, is not present.
1946
+ In this piece of code, we take ` file ` (which has the type
1947
+ ` Option<String> ` ), and convert it to a type that ` search ` can use, in
1948
+ this case, ` &Option<AsRef<Path>> ` . To do this, we take a reference of
1949
+ file, and map ` Path::new ` onto it. In this case, ` as_ref() ` converts
1950
+ the ` Option<String> ` into an ` Option<&str> ` , and from there, we can
1951
+ execute ` Path::new ` to the content of the optional, and return the
1952
+ optional of the new value. Once we have that, it is a simple matter of
1953
+ getting the ` city ` argument and executing ` search ` .
1951
1954
1952
1955
Modifying ` search ` is slightly trickier. The ` csv ` crate can build a
1953
1956
parser out of
@@ -1997,8 +2000,6 @@ enum CliError {
1997
2000
And now for impls on ` Display ` and ` Error ` :
1998
2001
1999
2002
``` rust,ignore
2000
- use std::fmt;
2001
-
2002
2003
impl fmt::Display for CliError {
2003
2004
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2004
2005
match *self {
@@ -2019,13 +2020,13 @@ impl Error for CliError {
2019
2020
}
2020
2021
}
2021
2022
2022
- fn cause(&self) -> Option<&Error> {
2023
- match *self {
2023
+ fn cause(&self) -> Option<&error:: Error> {
2024
+ match *self {
2024
2025
CliError::Io(ref err) => Some(err),
2025
- CliError::Csv (ref err) => Some(err),
2026
- // Our custom error doesn't have an underlying cause,
2027
- // but we could modify it so that it does.
2028
- CliError::NotFound => None,
2026
+ CliError::Parse (ref err) => Some(err),
2027
+ // Our custom error doesn't have an underlying cause, but we could
2028
+ // modify it so that it does.
2029
+ CliError::NotFound() => None,
2029
2030
}
2030
2031
}
2031
2032
}
@@ -2121,27 +2122,24 @@ string and add a flag to the Option variable. Once we've done that, Getopts does
2121
2122
2122
2123
``` rust,ignore
2123
2124
...
2124
- let mut opts = Options::new();
2125
- opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
2126
- opts.optflag("h", "help", "Show this usage message.");
2127
- opts.optflag("q", "quiet", "Silences errors and warnings.");
2125
+ let mut opts = Options::new();
2126
+ opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
2127
+ opts.optflag("h", "help", "Show this usage message.");
2128
+ opts.optflag("q", "quiet", "Silences errors and warnings.");
2128
2129
...
2129
2130
```
2130
2131
2131
2132
Now we only need to implement our “quiet” functionality. This requires us to
2132
2133
tweak the case analysis in ` main ` :
2133
2134
2134
2135
``` rust,ignore
2135
- use std::process;
2136
- ...
2137
- match search(&data_path, city) {
2138
- Err(CliError::NotFound) if matches.opt_present("q") => process::exit(1),
2139
- Err(err) => panic!("{}", err),
2140
- Ok(pops) => for pop in pops {
2141
- println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
2142
- }
2136
+ match search(&args.arg_data_path, &args.arg_city) {
2137
+ Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
2138
+ Err(err) => panic!("{}", err),
2139
+ Ok(pops) => for pop in pops {
2140
+ println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
2143
2141
}
2144
- ...
2142
+ }
2145
2143
```
2146
2144
2147
2145
Certainly, we don't want to be quiet if there was an IO error or if the data
0 commit comments