Newer
Older
// data derived from command arguments
/// Data of current command
/// can also include fetched information
#[derive(Default)]
pub struct Data {
// command line arguments
pub args: Args,
// rpc to substrate client
pub client: Option<Client>,
// graphql to duniter-indexer
pub indexer: Option<Indexer>,
// user address
pub address: Option<AccountId>,
// user keypair
pub keypair: Option<Pair>,
// user identity index
pub idty_index: Option<u32>,
// token decimals
pub token_decimals: u32,
// token symbol
pub token_symbol: String,
// genesis hash
pub genesis_hash: Hash,
// indexer genesis hash
pub indexer_genesis_hash: Hash,
}
/// system properties defined in client specs
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct SystemProperties {
token_decimals: u32,
token_symbol: String,
}
// implement helper functions for Data
impl Data {
/// --- constructor ---
pub fn new(args: Args) -> Self {
Self {
args,
cfg: confy::load("gcli", None).expect("must be able to build config"),
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
token_decimals: 0,
token_symbol: "tokens".into(),
..Default::default()
}
}
// --- getters ---
// the "unwrap" should not fail if data is well prepared
pub fn client(&self) -> &Client {
self.client.as_ref().expect("must build client first")
}
pub fn indexer(&self) -> &Indexer {
self.indexer.as_ref().expect("indexer is not set up")
}
pub fn address(&self) -> AccountId {
self.address.clone().expect("an address is needed")
}
pub fn keypair(&self) -> Pair {
self.keypair.clone().expect("a keypair is needed")
}
pub fn idty_index(&self) -> u32 {
self.idty_index.expect("must fetch idty index first")
}
// --- methods ---
pub fn format_balance(&self, amount: Balance) -> String {
let base: u32 = 10;
format!(
"{} {}",
(amount as f32) / (base.pow(self.token_decimals) as f32),
self.token_symbol
)
}
// --- mutators ---
/// force an address if needed
pub fn build_address(mut self) -> Self {
self.address = Some(
get_keys(
self.args.secret_format,
&self.args.address,
&self.args.secret,
NeededKeys::Public,
)
.expect("needed")
.0
.expect("needed"),
);
self
}
/// force a keypair if needed
pub fn build_keypair(mut self) -> Self {
let (address, keypair) = get_keys(
self.args.secret_format,
&self.args.address,
&self.args.secret,
NeededKeys::Secret,
)
.expect("needed");
self.address = address;
self.keypair = keypair;
self
}
/// build a client from url
// TODO get client from a pre-defined list
pub async fn build_client(mut self) -> Self {
self.client = Some(Client::from_url(&self.args.url).await.unwrap_or_else(|_| {
panic!(
"could not establish connection with the server {}",
self.args.url
)
}));
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
self
}
/// build an indexer if not disabled
// TODO check that indexer matches client
pub fn build_indexer(mut self) -> Result<Self, anyhow::Error> {
self.indexer = if self.args.no_indexer {
None
} else {
Some(Indexer {
gql_client: reqwest::Client::builder()
.user_agent("gcli/0.1.0")
.build()?,
gql_url: self.args.indexer.clone(),
})
};
Ok(self)
}
/// get issuer index
/// needs address and client first
pub async fn fetch_idty_index(mut self) -> Result<Self, anyhow::Error> {
self.idty_index = Some(
commands::identity::get_idty_index_by_account_id(self.client(), &self.address())
.await?
.ok_or(anyhow::anyhow!("needs to be member to use this command"))?,
);
Ok(self)
}
/// get genesis hash
pub async fn fetch_genesis_hash(mut self) -> Result<Self, anyhow::Error> {
self.genesis_hash = self
.client()
.storage()
.fetch(&runtime::storage().system().block_hash(0), None)
.await?
.unwrap();
Ok(self)
}
/// get indexer genesis hash
pub async fn fetch_indexer_genesis_hash(mut self) -> Result<Self, anyhow::Error> {
self.indexer_genesis_hash = self.indexer().fetch_genesis_hash().await?;
Ok(self)
}
/// get properties
pub async fn fetch_system_properties(mut self) -> Result<Self, anyhow::Error> {
let system_properties = self.client().rpc().system_properties().await?;
let system_properties = serde_json::from_value::<SystemProperties>(
serde_json::Value::Object(system_properties),
)?;
self.token_decimals = system_properties.token_decimals;
self.token_symbol = system_properties.token_symbol;
Ok(self)
}
// TODO prevent awaits in async methods, prefer register requests and execute them all at once with a join
// example below
// example!(
// use futures::join;
// let addr_idty_index = runtime::storage().identity().identity_index_of(&account_id);
// let addr_block_hash = runtime::storage().system().block_hash(0);
// // Multiple fetches can be done in parallel.
// let (idty_index, genesis_hash) = join!(
// api.storage().fetch(&addr_idty_index, None),
// api.storage().fetch(&addr_block_hash, None)
// );
// );
}