reth_cli/
chainspec.rs

1use std::{fs, path::PathBuf, sync::Arc};
2
3use clap::builder::TypedValueParser;
4
5#[derive(Debug, Clone)]
6struct Parser<C>(std::marker::PhantomData<C>);
7
8impl<C: ChainSpecParser> TypedValueParser for Parser<C> {
9    type Value = Arc<C::ChainSpec>;
10
11    fn parse_ref(
12        &self,
13        _cmd: &clap::Command,
14        arg: Option<&clap::Arg>,
15        value: &std::ffi::OsStr,
16    ) -> Result<Self::Value, clap::Error> {
17        let val =
18            value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
19        C::parse(val).map_err(|err| {
20            let arg = arg.map(|a| a.to_string()).unwrap_or_else(|| "...".to_owned());
21            let possible_values = C::SUPPORTED_CHAINS.join(",");
22            let msg = format!(
23                "Invalid value '{val}' for {arg}: {err}.\n    [possible values: {possible_values}]"
24            );
25            clap::Error::raw(clap::error::ErrorKind::InvalidValue, msg)
26        })
27    }
28}
29
30/// Trait for parsing chain specifications.
31///
32/// This trait extends [`clap::builder::TypedValueParser`] to provide a parser for chain
33/// specifications. Implementers of this trait must provide a list of supported chains and a
34/// function to parse a given string into a chain spec.
35pub trait ChainSpecParser: Clone + Send + Sync + 'static {
36    /// The chain specification type.
37    type ChainSpec: std::fmt::Debug + Send + Sync;
38
39    /// List of supported chains.
40    const SUPPORTED_CHAINS: &'static [&'static str];
41
42    /// The default value for the chain spec parser.
43    fn default_value() -> Option<&'static str> {
44        Self::SUPPORTED_CHAINS.first().copied()
45    }
46
47    /// Parses the given string into a chain spec.
48    ///
49    /// # Arguments
50    ///
51    /// * `s` - A string slice that holds the chain spec to be parsed.
52    ///
53    /// # Errors
54    ///
55    /// This function will return an error if the input string cannot be parsed into a valid
56    /// chain spec.
57    fn parse(s: &str) -> eyre::Result<Arc<Self::ChainSpec>>;
58
59    /// Produces a [`TypedValueParser`] for this chain spec parser.
60    fn parser() -> impl TypedValueParser<Value = Arc<Self::ChainSpec>> {
61        Parser(std::marker::PhantomData::<Self>)
62    }
63
64    /// Produces a help message for the chain spec argument.
65    fn help_message() -> String {
66        format!(
67            "The chain this node is running.\nPossible values are either a built-in chain or the path to a chain specification file.\n\nBuilt-in chains:\n    {}",
68            Self::SUPPORTED_CHAINS.join(", ")
69        )
70    }
71}
72
73/// A helper to parse a [`Genesis`](alloy_genesis::Genesis) as argument or from disk.
74pub fn parse_genesis(s: &str) -> eyre::Result<alloy_genesis::Genesis> {
75    // try to read json from path first
76    let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) {
77        Ok(raw) => raw,
78        Err(io_err) => {
79            // valid json may start with "\n", but must contain "{"
80            if s.contains('{') {
81                s.to_string()
82            } else {
83                return Err(io_err.into()) // assume invalid path
84            }
85        }
86    };
87
88    Ok(serde_json::from_str(&raw)?)
89}