fedimint_meta_client/
cli.rs

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::str::FromStr as _;
use std::{ffi, iter};

use anyhow::Context as _;
use clap::Parser;
use fedimint_meta_common::{MetaConsensusValue, MetaKey, MetaValue, DEFAULT_META_KEY};
use serde::Serialize;
use serde_json::json;

use super::MetaClientModule;
use crate::api::MetaFederationApi;

#[derive(Parser, Serialize)]
enum Opts {
    /// Get current consensus value
    Get {
        #[arg(long, default_value_t = DEFAULT_META_KEY)]
        key: MetaKey,
        #[arg(long)]
        hex: bool,
    },
    /// Get current consensus value revision
    GetRev {
        #[arg(long, default_value_t = DEFAULT_META_KEY)]
        key: MetaKey,
    },
    /// Get value change submissions
    GetSubmissions {
        #[arg(long, default_value_t = DEFAULT_META_KEY)]
        key: MetaKey,
        #[arg(long)]
        hex: bool,
    },
    /// Submit value change proposal
    Submit {
        #[arg(long, default_value_t = DEFAULT_META_KEY)]
        key: MetaKey,
        value: String,
        #[arg(long)]
        hex: bool,
    },
}

pub(crate) async fn handle_cli_command(
    meta: &MetaClientModule,
    args: &[ffi::OsString],
) -> anyhow::Result<serde_json::Value> {
    let opts = Opts::parse_from(iter::once(&ffi::OsString::from("meta")).chain(args.iter()));

    let res = match opts {
        Opts::Get { key, hex } => {
            if let Some(MetaConsensusValue { revision, value }) =
                meta.module_api.get_consensus(key).await?
            {
                let value = if hex {
                    serde_json::to_value(value).expect("can't fail")
                } else {
                    value
                        .to_json_lossy()
                        .context("deserializing consensus value as json")?
                };
                json!({
                    "revision": revision,
                    "value": value
                })
            } else {
                serde_json::Value::Null
            }
        }
        Opts::GetRev { key } => {
            if let Some(rev) = meta.module_api.get_consensus_rev(key).await? {
                json!({
                    "revision": rev,
                })
            } else {
                serde_json::Value::Null
            }
        }
        Opts::GetSubmissions { key, hex } => {
            let submissions = meta
                .module_api
                .get_submissions(key, meta.admin_auth()?)
                .await?;
            let submissions: serde_json::Map<String, serde_json::Value> = submissions
                .into_iter()
                .map(|(peer_id, value)| -> anyhow::Result<_> {
                    let value = if hex {
                        serde_json::Value::String(value.to_string())
                    } else {
                        serde_json::from_reader(value.as_slice())
                            .context("deserializing submission value")?
                    };

                    Ok((peer_id.to_string(), value))
                })
                .collect::<anyhow::Result<_, _>>()?;

            serde_json::Value::Object(submissions)
        }
        Opts::Submit { key, value, hex } => {
            let value: MetaValue = if hex {
                MetaValue::from_str(&value).context("value not a valid hex string")?
            } else {
                let _valid_json: serde_json::Value =
                    serde_json::from_str(&value).context("value not a valid json string")?;
                MetaValue::from(value.as_bytes())
            };

            meta.module_api
                .submit(key, value, meta.admin_auth()?)
                .await?;

            serde_json::Value::Bool(true)
        }
    };

    Ok(res)
}