]> git.lizzy.rs Git - minetest-servers.git/blob - src/main.rs
Initial commit
[minetest-servers.git] / src / main.rs
1 use anyhow::anyhow;
2 use clap::CommandFactory;
3 use clap::Parser;
4 use itertools::Itertools;
5 use serde::Deserialize;
6 use serde_json::Value as JsonValue;
7 use std::{
8     collections::{BTreeMap, BTreeSet},
9     fmt,
10 };
11
12 fn main() -> anyhow::Result<()> {
13     #[derive(Parser, Debug)]
14     #[clap(author, version, about, long_about = None)]
15     struct Args {
16         /// Address of the server list
17         #[clap(short, long, value_parser, default_value_t = String::from("https://servers.minetest.net/list"))]
18         address: String,
19
20         /// List available keys
21         #[clap(short, long, action)]
22         show_keys: bool,
23
24         /// The key to look up. Use --show-keys to list available keys.
25         key: Option<String>,
26     }
27     let args = Args::parse();
28
29     #[derive(Deserialize)]
30     struct Payload {
31         list: Vec<BTreeMap<String, JsonValue>>,
32     }
33     let payload: Payload = reqwest::blocking::get(args.address)?.json()?;
34
35     if args.show_keys {
36         let keys: BTreeSet<&String> = payload.list.iter().flat_map(BTreeMap::keys).collect();
37         println!("available keys: {}", keys.iter().format(", "));
38     }
39
40     if let Some(key) = args.key {
41         let mut iter = payload
42             .list
43             .iter()
44             .flat_map(|server| server.get(&key).map(|value| (server, value)))
45             .peekable();
46
47         if iter.peek().is_none() {
48             return Err(anyhow!("invalid key"));
49         } else {
50             iter.for_each(|(server, value)| {
51                 struct DisplayJson<'a>(Option<&'a JsonValue>);
52                 impl<'a> fmt::Display for DisplayJson<'a> {
53                     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54                         match self.0 {
55                             None => Ok(()),
56                             Some(value) => match value {
57                                 JsonValue::String(x) => write!(f, "{x}"),
58                                 _ => write!(f, "{value}"),
59                             },
60                         }
61                     }
62                 }
63
64                 let address = DisplayJson(server.get("address"));
65                 let port = DisplayJson(server.get("port"));
66
67                 if let JsonValue::Array(array) = value {
68                     let prefix = format!("{address}:{port}"); // cache it
69                     for value in array.iter() {
70                         println!("{prefix}\t{}", DisplayJson(Some(value)));
71                     }
72                 } else {
73                     println!("{address}:{port}\t{}", DisplayJson(Some(value)));
74                 }
75             });
76         }
77     } else if !args.show_keys {
78         Args::command().print_help()?;
79     }
80
81     Ok(())
82 }