1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! A somewhat fancy example of a typical `bpaf` usage.
//!
//! Most important thing is probably a notion of "A pair of values, where each value can be one of
//! two different values", here it's distance which can be miles and kilometers and speed - miles
//! and kilometers per hours. Generated usage line can give a better hint to what's going on
use bpaf::*;
#[derive(Clone, Debug)]
#[allow(dead_code)]
enum Segment {
Time(f64),
SpeedDistance { speed: f64, dist: f64 },
}
fn main() {
// Suppose we want to calcualte a total time of a travel, where parts of
// a travel can be given either as pairs of speed and distance or just by time.
// Speed can be given by KPH or MPH. Distance - either miles or km.
// parsers for speeds. Both speeds are converted to the same units
let mph = long("mph")
.help("speed in MPH")
.argument::<f64>("SPEED")
.map(|x| x * 1.6);
let kph = long("kph").help("Speed in KPH").argument::<f64>("SPEED");
// speed is either kph or mph, conversion to mph is handled by the parser
let speed = construct!([mph, kph]);
// parsers for distances, both are converted to the same units
let km = long("km").help("Distance in KM").argument::<f64>("KMs");
let mi = long("mi")
.help("distance in miles")
.argument::<f64>("MILES")
.map(|x| x * 1.6);
let dist = construct!([mi, km]);
// time, presumably in seconds
let time = long("time")
.help("Travel time in hours")
.argument::<f64>("TIME");
// parsed time is trivially converted to time segment
let segment_time = time.map(Segment::Time);
// parsed speed/distance is packed into SpeedDistance segment
let segment_speed = construct!(Segment::SpeedDistance { speed, dist });
// segment can be either of two defined
let segment = construct!([segment_speed, segment_time]);
// and we have several of them.
let parser = segment
.many()
.guard(|x| !x.is_empty(), "need at least one segment")
.guard(
|x| x.len() < 10,
"for more than 9 segments you need to purchase a premium subscription",
);
let descr = "Accepts one or more travel segments";
let header = "You need to specify one or more travel segments, segment is defined by
a pair of speed and distance or by time.
This example defines two separate travel segments, one given by speed/distance combo and one by time
travel --km 180 --kph 35 --time";
let decorated = parser.to_options().descr(descr).header(header);
// help message tries to explain what's needed:
// either --time OR one speed and one distance, both can be given in miles or km.
// number of speed flags must correspond to number of distance flags, more or
// less results in parser error messages
let opt = decorated.run();
println!("{:#?}", opt);
}