2 use clap::CommandFactory;
4 use itertools::Itertools;
5 use serde::Deserialize;
6 use serde_json::Value as JsonValue;
8 collections::{BTreeMap, BTreeSet},
12 fn main() -> anyhow::Result<()> {
13 #[derive(Parser, Debug)]
14 #[clap(author, version, about, long_about = None)]
16 /// Address of the server list
17 #[clap(short, long, value_parser, default_value_t = String::from("https://servers.minetest.net/list"))]
20 /// List available keys
21 #[clap(short, long, action)]
24 /// The key to look up. Use --show-keys to list available keys.
27 let args = Args::parse();
29 #[derive(Deserialize)]
31 list: Vec<BTreeMap<String, JsonValue>>,
33 let payload: Payload = reqwest::blocking::get(args.address)?.json()?;
36 let keys: BTreeSet<&String> = payload.list.iter().flat_map(BTreeMap::keys).collect();
37 println!("available keys: {}", keys.iter().format(", "));
40 if let Some(key) = args.key {
41 let mut iter = payload
44 .flat_map(|server| server.get(&key).map(|value| (server, value)))
47 if iter.peek().is_none() {
48 return Err(anyhow!("invalid key"));
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 {
56 Some(value) => match value {
57 JsonValue::String(x) => write!(f, "{x}"),
58 _ => write!(f, "{value}"),
64 let address = DisplayJson(server.get("address"));
65 let port = DisplayJson(server.get("port"));
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)));
73 println!("{address}:{port}\t{}", DisplayJson(Some(value)));
77 } else if !args.show_keys {
78 Args::command().print_help()?;