aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 9986096f7c55e01d630f30ebe26f446b84c9eb6c (plain)
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
use anyhow::anyhow;
use clap::CommandFactory;
use clap::Parser;
use itertools::Itertools;
use serde::Deserialize;
use serde_json::Value as JsonValue;
use std::{
    collections::{BTreeMap, BTreeSet},
    fmt,
};

fn main() -> anyhow::Result<()> {
    #[derive(Parser, Debug)]
    #[clap(author, version, about, long_about = None)]
    struct Args {
        /// Address of the server list
        #[clap(short, long, value_parser, default_value_t = String::from("https://servers.minetest.net/list"))]
        address: String,

        /// List available keys
        #[clap(short, long, action)]
        show_keys: bool,

        /// The key to look up. Use --show-keys to list available keys.
        key: Option<String>,
    }
    let args = Args::parse();

    #[derive(Deserialize)]
    struct Payload {
        list: Vec<BTreeMap<String, JsonValue>>,
    }
    let payload: Payload = reqwest::blocking::get(args.address)?.json()?;

    if args.show_keys {
        let keys: BTreeSet<&String> = payload.list.iter().flat_map(BTreeMap::keys).collect();
        println!("available keys: {}", keys.iter().format(", "));
    }

    if let Some(key) = args.key {
        let mut iter = payload
            .list
            .iter()
            .flat_map(|server| server.get(&key).map(|value| (server, value)))
            .peekable();

        if iter.peek().is_none() {
            return Err(anyhow!("invalid key"));
        } else {
            iter.for_each(|(server, value)| {
                struct DisplayJson<'a>(Option<&'a JsonValue>);
                impl<'a> fmt::Display for DisplayJson<'a> {
                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                        match self.0 {
                            None => Ok(()),
                            Some(value) => match value {
                                JsonValue::String(x) => write!(f, "{x}"),
                                _ => write!(f, "{value}"),
                            },
                        }
                    }
                }

                let address = DisplayJson(server.get("address"));
                let port = DisplayJson(server.get("port"));

                if let JsonValue::Array(array) = value {
                    let prefix = format!("{address}:{port}"); // cache it
                    for value in array.iter() {
                        println!("{prefix}\t{}", DisplayJson(Some(value)));
                    }
                } else {
                    println!("{address}:{port}\t{}", DisplayJson(Some(value)));
                }
            });
        }
    } else if !args.show_keys {
        Args::command().print_help()?;
    }

    Ok(())
}