travel/
travel.rs

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