Skip to main content

soil_cli/params/
mod.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7mod database_params;
8mod import_params;
9mod keystore_params;
10mod message_params;
11mod mixnet_params;
12mod network_params;
13mod node_key_params;
14mod offchain_worker_params;
15mod prometheus_params;
16mod pruning_params;
17mod rpc_params;
18mod runtime_params;
19mod shared_params;
20mod telemetry_params;
21mod transaction_pool_params;
22
23use crate::arg_enums::{CryptoScheme, OutputType};
24use clap::Args;
25use soil_service::config::{IpNetwork, RpcBatchRequestConfig};
26use std::{fmt::Debug, str::FromStr};
27use subsoil::core::crypto::{Ss58AddressFormat, Ss58AddressFormatRegistry};
28use subsoil::runtime::{
29	generic::BlockId,
30	traits::{Block as BlockT, NumberFor},
31};
32
33pub use crate::params::{
34	database_params::*, import_params::*, keystore_params::*, message_params::*, mixnet_params::*,
35	network_params::*, node_key_params::*, offchain_worker_params::*, prometheus_params::*,
36	pruning_params::*, rpc_params::*, runtime_params::*, shared_params::*, telemetry_params::*,
37	transaction_pool_params::*,
38};
39
40/// Parse Ss58AddressFormat
41pub fn parse_ss58_address_format(x: &str) -> Result<Ss58AddressFormat, String> {
42	match Ss58AddressFormatRegistry::try_from(x) {
43		Ok(format_registry) => Ok(format_registry.into()),
44		Err(_) => Err(format!(
45			"Unable to parse variant. Known variants: {:?}",
46			Ss58AddressFormat::all_names()
47		)),
48	}
49}
50
51/// Wrapper type of `String` that holds an unsigned integer of arbitrary size, formatted as a
52/// decimal.
53#[derive(Debug, Clone)]
54pub struct GenericNumber(String);
55
56impl FromStr for GenericNumber {
57	type Err = String;
58
59	fn from_str(block_number: &str) -> Result<Self, Self::Err> {
60		if let Some(pos) = block_number.chars().position(|d| !d.is_digit(10)) {
61			Err(format!("Expected block number, found illegal digit at position: {}", pos))
62		} else {
63			Ok(Self(block_number.to_owned()))
64		}
65	}
66}
67
68impl GenericNumber {
69	/// Wrapper on top of `std::str::parse<N>` but with `Error` as a `String`
70	///
71	/// See `https://doc.rust-lang.org/std/primitive.str.html#method.parse` for more elaborate
72	/// documentation.
73	pub fn parse<N>(&self) -> Result<N, String>
74	where
75		N: FromStr,
76		N::Err: std::fmt::Debug,
77	{
78		FromStr::from_str(&self.0).map_err(|e| format!("Failed to parse block number: {:?}", e))
79	}
80}
81
82/// Wrapper type that is either a `Hash` or the number of a `Block`.
83#[derive(Debug, Clone)]
84pub struct BlockNumberOrHash(String);
85
86impl FromStr for BlockNumberOrHash {
87	type Err = String;
88
89	fn from_str(block_number: &str) -> Result<Self, Self::Err> {
90		if let Some(rest) = block_number.strip_prefix("0x") {
91			if let Some(pos) = rest.chars().position(|c| !c.is_ascii_hexdigit()) {
92				Err(format!(
93					"Expected block hash, found illegal hex character at position: {}",
94					2 + pos,
95				))
96			} else {
97				Ok(Self(block_number.into()))
98			}
99		} else {
100			GenericNumber::from_str(block_number).map(|v| Self(v.0))
101		}
102	}
103}
104
105impl BlockNumberOrHash {
106	/// Parse the inner value as `BlockId`.
107	pub fn parse<B: BlockT>(&self) -> Result<BlockId<B>, String>
108	where
109		<B::Hash as FromStr>::Err: std::fmt::Debug,
110		NumberFor<B>: FromStr,
111		<NumberFor<B> as FromStr>::Err: std::fmt::Debug,
112	{
113		if self.0.starts_with("0x") {
114			Ok(BlockId::Hash(
115				FromStr::from_str(&self.0[2..])
116					.map_err(|e| format!("Failed to parse block hash: {:?}", e))?,
117			))
118		} else {
119			GenericNumber(self.0.clone()).parse().map(BlockId::Number)
120		}
121	}
122}
123
124/// Optional flag for specifying crypto algorithm
125#[derive(Debug, Clone, Args)]
126pub struct CryptoSchemeFlag {
127	/// cryptography scheme
128	#[arg(long, value_name = "SCHEME", value_enum, ignore_case = true, default_value_t = CryptoScheme::Sr25519)]
129	pub scheme: CryptoScheme,
130}
131
132/// Optional flag for specifying output type
133#[derive(Debug, Clone, Args)]
134pub struct OutputTypeFlag {
135	/// output format
136	#[arg(long, value_name = "FORMAT", value_enum, ignore_case = true, default_value_t = OutputType::Text)]
137	pub output_type: OutputType,
138}
139
140/// Optional flag for specifying network scheme
141#[derive(Debug, Clone, Args)]
142pub struct NetworkSchemeFlag {
143	/// network address format
144	#[arg(
145		short = 'n',
146		long,
147		value_name = "NETWORK",
148		ignore_case = true,
149		value_parser = parse_ss58_address_format,
150	)]
151	pub network: Option<Ss58AddressFormat>,
152}
153
154#[cfg(test)]
155mod tests {
156	use super::*;
157
158	type Header = subsoil::runtime::generic::Header<u32, subsoil::runtime::traits::BlakeTwo256>;
159	type Block = subsoil::runtime::generic::Block<Header, subsoil::runtime::OpaqueExtrinsic>;
160
161	#[test]
162	fn parse_block_number() {
163		let block_number_or_hash = BlockNumberOrHash::from_str("1234").unwrap();
164		let parsed = block_number_or_hash.parse::<Block>().unwrap();
165		assert_eq!(BlockId::Number(1234), parsed);
166	}
167
168	#[test]
169	fn parse_block_hash() {
170		let hash = subsoil::core::H256::default();
171		let hash_str = format!("{:?}", hash);
172		let block_number_or_hash = BlockNumberOrHash::from_str(&hash_str).unwrap();
173		let parsed = block_number_or_hash.parse::<Block>().unwrap();
174		assert_eq!(BlockId::Hash(hash), parsed);
175	}
176
177	#[test]
178	fn parse_block_hash_fails() {
179		assert_eq!(
180			"Expected block hash, found illegal hex character at position: 2",
181			BlockNumberOrHash::from_str("0xHello").unwrap_err(),
182		);
183	}
184
185	#[test]
186	fn parse_block_number_fails() {
187		assert_eq!(
188			"Expected block number, found illegal digit at position: 3",
189			BlockNumberOrHash::from_str("345Hello").unwrap_err(),
190		);
191	}
192}