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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use anyhow::Result;
use clap::{App, Arg, ArgMatches};
use libworterbuch::{
    client::config::Config,
    codec::{Err, KeyValuePair, PState, State},
};

pub fn print_pstate(msg: &PState, json: bool) {
    if json {
        match serde_json::to_string(msg) {
            Ok(json) => println!("{json}"),
            Err(e) => {
                eprintln!("Error converting message to json: {e}");
            }
        }
    } else {
        for KeyValuePair { key, value } in &msg.key_value_pairs {
            println!("{key}={value}");
        }
    }
}

pub fn print_state(msg: &State, json: bool) {
    if json {
        match serde_json::to_string(msg) {
            Ok(json) => println!("{json}"),
            Err(e) => {
                eprintln!("Error converting message to json: {e}");
            }
        }
    } else {
        let KeyValuePair { key, value } = &msg.key_value;
        println!("{key}={value}");
    }
}

pub fn print_err(msg: &Err, json: bool) {
    if json {
        match serde_json::to_string(msg) {
            Ok(json) => println!("{json}"),
            Err(e) => {
                eprintln!("Error converting message to json: {e}");
            }
        }
    } else {
        eprintln!("server error {}: {}", msg.error_code, msg.metadata);
    }
}

pub fn app<'help>(
    name: &'help str,
    about: &'help str,
    include_json: bool,
    args: Vec<Arg<'help>>,
) -> Result<(ArgMatches, String, String, u16, bool)> {
    let config = Config::new()?;

    let mut app = App::new(name)
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(about);
    for arg in default_args(include_json).iter().chain(args.iter()) {
        app = app.arg(arg)
    }

    let matches = app.get_matches();

    let default_proto = config.proto;
    let default_host_addr = config.host_addr;
    let default_port = config.port;

    let proto = default_proto;
    let host_addr = matches
        .get_one::<String>("ADDR")
        .map(ToOwned::to_owned)
        .unwrap_or(default_host_addr);
    let port = matches
        .get_one::<u16>("PORT")
        .map(ToOwned::to_owned)
        .unwrap_or(default_port);

    let json = if include_json {
        matches.is_present("JSON")
    } else {
        false
    };

    Ok((matches, proto, host_addr, port, json))
}

fn default_args<'help>(include_json: bool) -> Vec<Arg<'help>> {
    let mut args = vec![
        Arg::with_name("ADDR")
            .short('a')
            .long("addr")
            .help("The address of the Wörterbuch server. When omitted, the value of the env var WORTERBUCH_HOST_ADDRESS will be used. If that is not set, 127.0.0.1 will be used.")
            .takes_value(true)
            .required(false),
        Arg::with_name("PORT")
            .short('p')
            .long("port")
            .help("The port of the Wörterbuch server. When omitted, the value of the env var WORTERBUCH_PORT will be used. If that is not set, 4242 will be used.")
            .takes_value(true)
            .required(false),
    ];

    if include_json {
        args.push(
            Arg::with_name("JSON")
                .short('j')
                .long("json")
                .help("Output data in JSON format instead of '[key]=[value]' pairs.")
                .takes_value(false)
                .required(false),
        );
    }

    args
}