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
78
79
80
//! This example constructs dynamic command tree from a structure

use bpaf::*;

#[derive(Debug, Clone)]
enum Cog {
    Command {
        help: &'static str,
        name: &'static str,
        operation: &'static str,
    },
    Group {
        name: &'static str,
        help: &'static str,
        nested: Vec<Cog>,
    },
}

fn config() -> Cog {
    let echo1 = Cog::Command {
        help: "First echo command",
        name: "echoCommand",
        operation: "echo 'some text'",
    };

    let echo2 = Cog::Command {
        help: "Second echo command",
        name: "anotherEchoCmd",
        operation: "echo 'another text'",
    };
    let sleep = Cog::Command {
        name: "sleepCommand",
        help: "sleep for a bit",
        operation: "sleep 5",
    };
    let group1 = Cog::Group {
        name: "commandGroup",
        help: "contains a single sleep",
        nested: vec![sleep],
    };
    Cog::Group {
        name: "commands",
        help: "contains all the commands, can be flattened with choose but effort :)",
        nested: vec![echo1, echo2, group1],
    }
}

fn choose<T>(xs: Vec<Box<dyn Parser<T> + 'static>>) -> Box<dyn Parser<T>>
where
    T: 'static,
{
    let mut items = xs.into_iter();

    let mut res = items.next().unwrap();
    for next in items {
        res = construct!([res, next]).boxed()
    }
    res
}

fn make_parser(item: &Cog) -> Box<dyn Parser<&'static str>> {
    match item {
        Cog::Command {
            help,
            name,
            operation,
        } => Box::new(pure(*operation).to_options().descr(*help).command(name)),
        Cog::Group { name, help, nested } => {
            let nested = nested.iter().map(make_parser).collect::<Vec<_>>();
            let inner = choose(nested);
            inner.to_options().descr(*help).command(name).boxed()
        }
    }
}

fn main() {
    let cfg = config();
    let command = make_parser(&cfg).to_options().run();
    println!("{:?}", command);
}