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
30pub trait ChainSpecParser: Clone + Send + Sync + 'static {
36 type ChainSpec: std::fmt::Debug + Send + Sync;
38
39 const SUPPORTED_CHAINS: &'static [&'static str];
41
42 fn default_value() -> Option<&'static str> {
44 Self::SUPPORTED_CHAINS.first().copied()
45 }
46
47 fn parse(s: &str) -> eyre::Result<Arc<Self::ChainSpec>>;
58
59 fn parser() -> impl TypedValueParser<Value = Arc<Self::ChainSpec>> {
61 Parser(std::marker::PhantomData::<Self>)
62 }
63
64 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
73pub fn parse_genesis(s: &str) -> eyre::Result<alloy_genesis::Genesis> {
75 let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) {
77 Ok(raw) => raw,
78 Err(io_err) => {
79 if s.contains('{') {
81 s.to_string()
82 } else {
83 return Err(io_err.into()) }
85 }
86 };
87
88 Ok(serde_json::from_str(&raw)?)
89}