frequency_cli/
export_metadata_cmd.rs

1use clap::Parser;
2use sc_cli::{CliConfiguration, Error, GenericNumber, SharedParams};
3use sc_client_api::HeaderBackend;
4use serde_json::{json, to_writer};
5use sp_api::{Metadata, ProvideRuntimeApi};
6use sp_core::Bytes;
7use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
8use std::{fmt::Debug, fs, io, path::PathBuf, str::FromStr, sync::Arc};
9
10/// The `export-metadata` command used to export chain metadata.
11/// Remember that this uses the chain database. So it will pull the _current_ metadata from that database.
12#[derive(Debug, Clone, Parser)]
13pub struct ExportMetadataCmd {
14	/// Output file name or stdout if unspecified.
15	#[clap(value_parser)]
16	pub output: Option<PathBuf>,
17
18	/// Specify starting block number.
19	///
20	/// Default is 0.
21	#[clap(long, value_name = "BLOCK")]
22	pub from: Option<GenericNumber>,
23
24	#[allow(missing_docs)]
25	#[clap(flatten)]
26	pub shared_params: SharedParams,
27
28	/// Use a temporary directory for the db
29	///
30	/// A temporary directory will be created to store the configuration and will be deleted
31	/// at the end of the process.
32	///
33	/// Note: the directory is random per process execution. This directory is used as base path
34	/// which includes: database, node key and keystore.
35	#[arg(long, conflicts_with = "base_path")]
36	pub tmp: bool,
37}
38
39#[allow(clippy::unwrap_used)]
40impl ExportMetadataCmd {
41	/// Run the export-metadata command
42	pub async fn run<B, C>(&self, client: Arc<C>) -> Result<(), Error>
43	where
44		B: BlockT,
45		C: ProvideRuntimeApi<B> + HeaderBackend<B>,
46		C::Api: Metadata<B> + 'static,
47		<<B::Header as HeaderT>::Number as FromStr>::Err: Debug,
48	{
49		let api = client.runtime_api();
50
51		let block_number = self.from.as_ref().and_then(|f| f.parse().ok()).unwrap_or(0u32);
52		let maybe_hash = client.hash(block_number.into())?;
53		let block_hash = maybe_hash.ok_or_else(|| Error::from("Block not found"))?;
54
55		let metadata: Bytes = api.metadata_at_version(block_hash, 15u32).unwrap().unwrap().into();
56		let result = json!({ "result": metadata });
57
58		let file: Box<dyn io::Write> = match &self.output {
59			Some(filename) => Box::new(fs::File::create(filename)?),
60			None => Box::new(io::stdout()),
61		};
62		to_writer(file, &result).map_err(|_| Error::from("Failed Encoding"))
63	}
64}
65
66impl CliConfiguration for ExportMetadataCmd {
67	fn shared_params(&self) -> &SharedParams {
68		&self.shared_params
69	}
70
71	// Enabling `--tmp` on this command
72	fn base_path(&self) -> Result<Option<sc_service::BasePath>, sc_cli::Error> {
73		match &self.tmp {
74			true => Ok(Some(sc_service::BasePath::new_temp_dir()?)),
75			false => self.shared_params.base_path(),
76		}
77	}
78}