dynamic/
dynamic.rs

1//! You can construct parser at runtime without having a concrete type too
2
3use bpaf::*;
4
5#[derive(Debug, Clone)]
6enum Value {
7    Bool(bool),
8    Number(usize),
9    String(String),
10}
11
12fn number(name: &'static str) -> impl Parser<(String, Value)> {
13    let label = name.to_string();
14    long(name)
15        .argument::<usize>("NUM")
16        .map(move |n| (label.clone(), Value::Number(n)))
17}
18
19fn bool(name: &'static str) -> impl Parser<(String, Value)> {
20    let label = name.to_string();
21    long(name)
22        .switch()
23        .map(move |n| (label.clone(), Value::Bool(n)))
24}
25
26fn string(name: &'static str) -> impl Parser<(String, Value)> {
27    let label = name.to_string();
28    long(name)
29        .help("this can use a help message")
30        .argument::<String>("NUM")
31        .map(move |n| (label.clone(), Value::String(n)))
32}
33
34fn cons<T>(acc: Box<dyn Parser<Vec<T>>>, cur: Box<dyn Parser<T>>) -> Box<dyn Parser<Vec<T>>>
35where
36    T: 'static,
37{
38    construct!(acc, cur)
39        .map(|(mut acc, cur)| {
40            acc.push(cur);
41            acc
42        })
43        .boxed()
44}
45
46enum Ty {
47    Bool,
48    Number,
49    String,
50}
51
52fn main() {
53    let items = &[
54        ("banana", Ty::Bool),
55        ("width", Ty::Number),
56        ("name", Ty::String),
57    ];
58
59    let mut parser = pure(Vec::<(String, Value)>::new()).boxed();
60    for (name, ty) in items {
61        parser = cons(
62            parser,
63            match ty {
64                Ty::Bool => bool(name).boxed(),
65                Ty::Number => number(name).boxed(),
66                Ty::String => string(name).boxed(),
67            },
68        )
69    }
70
71    let options = parser.run();
72    println!("{:?}", options);
73}