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);
}