fedimint_meta_client/
cli.rs

1use std::str::FromStr as _;
2use std::{ffi, iter};
3
4use anyhow::Context as _;
5use clap::Parser;
6use fedimint_meta_common::{DEFAULT_META_KEY, MetaConsensusValue, MetaKey, MetaValue};
7use serde::Serialize;
8use serde_json::json;
9
10use super::MetaClientModule;
11use crate::api::MetaFederationApi;
12
13#[derive(Parser, Serialize)]
14enum Opts {
15    /// Get current consensus value
16    Get {
17        #[arg(long, default_value_t = DEFAULT_META_KEY)]
18        key: MetaKey,
19        #[arg(long)]
20        hex: bool,
21    },
22    /// Get current consensus value revision
23    GetRev {
24        #[arg(long, default_value_t = DEFAULT_META_KEY)]
25        key: MetaKey,
26    },
27    /// Get value change submissions
28    GetSubmissions {
29        #[arg(long, default_value_t = DEFAULT_META_KEY)]
30        key: MetaKey,
31        #[arg(long)]
32        hex: bool,
33    },
34    /// Submit value change proposal
35    Submit {
36        #[arg(long, default_value_t = DEFAULT_META_KEY)]
37        key: MetaKey,
38        value: String,
39        #[arg(long)]
40        hex: bool,
41    },
42}
43
44pub(crate) async fn handle_cli_command(
45    meta: &MetaClientModule,
46    args: &[ffi::OsString],
47) -> anyhow::Result<serde_json::Value> {
48    let opts = Opts::parse_from(iter::once(&ffi::OsString::from("meta")).chain(args.iter()));
49
50    let res = match opts {
51        Opts::Get { key, hex } => match meta.module_api.get_consensus(key).await? {
52            Some(MetaConsensusValue { revision, value }) => {
53                let value = if hex {
54                    serde_json::to_value(value).expect("can't fail")
55                } else {
56                    value
57                        .to_json_lossy()
58                        .context("deserializing consensus value as json")?
59                };
60                json!({
61                    "revision": revision,
62                    "value": value
63                })
64            }
65            _ => serde_json::Value::Null,
66        },
67        Opts::GetRev { key } => match meta.module_api.get_consensus_rev(key).await? {
68            Some(rev) => {
69                json!({
70                    "revision": rev,
71                })
72            }
73            _ => serde_json::Value::Null,
74        },
75        Opts::GetSubmissions { key, hex } => {
76            let submissions = meta
77                .module_api
78                .get_submissions(key, meta.admin_auth()?)
79                .await?;
80            let submissions: serde_json::Map<String, serde_json::Value> = submissions
81                .into_iter()
82                .map(|(peer_id, value)| -> anyhow::Result<_> {
83                    let value = if hex {
84                        serde_json::Value::String(value.to_string())
85                    } else {
86                        serde_json::from_reader(value.as_slice())
87                            .context("deserializing submission value")?
88                    };
89
90                    Ok((peer_id.to_string(), value))
91                })
92                .collect::<anyhow::Result<_, _>>()?;
93
94            serde_json::Value::Object(submissions)
95        }
96        Opts::Submit { key, value, hex } => {
97            let value: MetaValue = if hex {
98                MetaValue::from_str(&value).context("value not a valid hex string")?
99            } else {
100                let _valid_json: serde_json::Value =
101                    serde_json::from_str(&value).context("value not a valid json string")?;
102                MetaValue::from(value.as_bytes())
103            };
104
105            meta.module_api
106                .submit(key, value, meta.admin_auth()?)
107                .await?;
108
109            serde_json::Value::Bool(true)
110        }
111    };
112
113    Ok(res)
114}