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