use std::{collections::HashSet, fmt, str::FromStr};
use serde::{Deserialize, Serialize, Serializer};
use strum::{
AsRefStr, EnumIter, IntoStaticStr, ParseError, VariantArray, VariantNames,
};
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub enum RpcModuleSelection {
All,
#[default]
Standard,
Evm,
Selection(HashSet<EthRpcModule>),
}
impl RpcModuleSelection {
pub const EVM_MODULES: [EthRpcModule; 1] = [EthRpcModule::Eth];
pub const STANDARD_MODULES: [EthRpcModule; 3] =
[EthRpcModule::Eth, EthRpcModule::Net, EthRpcModule::Web3];
pub fn all_modules() -> HashSet<EthRpcModule> {
EthRpcModule::modules().into_iter().collect()
}
pub fn standard_modules() -> HashSet<EthRpcModule> {
HashSet::from(Self::STANDARD_MODULES)
}
pub fn evm_modules() -> HashSet<EthRpcModule> {
HashSet::from(Self::EVM_MODULES)
}
pub fn default_ipc_modules() -> HashSet<EthRpcModule> {
Self::all_modules()
}
pub fn try_from_selection<I, T>(selection: I) -> Result<Self, T::Error>
where
I: IntoIterator<Item = T>,
T: TryInto<EthRpcModule>,
{
selection.into_iter().map(TryInto::try_into).collect()
}
pub fn len(&self) -> usize {
match self {
Self::All => EthRpcModule::variant_count(),
Self::Standard => Self::STANDARD_MODULES.len(),
Self::Evm => Self::EVM_MODULES.len(),
Self::Selection(s) => s.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::Selection(sel) => sel.is_empty(),
_ => false,
}
}
pub fn iter_selection(
&self,
) -> Box<dyn Iterator<Item = EthRpcModule> + '_> {
match self {
Self::All => Box::new(EthRpcModule::modules().into_iter()),
Self::Standard => Box::new(Self::STANDARD_MODULES.iter().copied()),
Self::Evm => Box::new(Self::EVM_MODULES.iter().copied()),
Self::Selection(s) => Box::new(s.iter().copied()),
}
}
pub fn to_selection(&self) -> HashSet<EthRpcModule> {
match self {
Self::All => Self::all_modules(),
Self::Standard => Self::standard_modules(),
Self::Evm => Self::evm_modules(),
Self::Selection(s) => s.clone(),
}
}
pub fn into_selection(self) -> HashSet<EthRpcModule> {
match self {
Self::All => Self::all_modules(),
Self::Standard => Self::standard_modules(),
Self::Evm => Self::evm_modules(),
Self::Selection(s) => s,
}
}
pub fn are_identical(http: Option<&Self>, ws: Option<&Self>) -> bool {
match (http, ws) {
(Some(Self::All), Some(other)) | (Some(other), Some(Self::All)) => {
other.len() == EthRpcModule::variant_count()
}
(Some(some), None) | (None, Some(some)) => some.is_empty(),
(Some(http), Some(ws)) => http.to_selection() == ws.to_selection(),
(None, None) => true,
}
}
}
impl From<&HashSet<EthRpcModule>> for RpcModuleSelection {
fn from(s: &HashSet<EthRpcModule>) -> Self { Self::from(s.clone()) }
}
impl From<HashSet<EthRpcModule>> for RpcModuleSelection {
fn from(s: HashSet<EthRpcModule>) -> Self { Self::Selection(s) }
}
impl From<&[EthRpcModule]> for RpcModuleSelection {
fn from(s: &[EthRpcModule]) -> Self {
Self::Selection(s.iter().copied().collect())
}
}
impl From<Vec<EthRpcModule>> for RpcModuleSelection {
fn from(s: Vec<EthRpcModule>) -> Self {
Self::Selection(s.into_iter().collect())
}
}
impl<const N: usize> From<[EthRpcModule; N]> for RpcModuleSelection {
fn from(s: [EthRpcModule; N]) -> Self {
Self::Selection(s.iter().copied().collect())
}
}
impl<'a> FromIterator<&'a EthRpcModule> for RpcModuleSelection {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = &'a EthRpcModule> {
iter.into_iter().copied().collect()
}
}
impl FromIterator<EthRpcModule> for RpcModuleSelection {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = EthRpcModule> {
Self::Selection(iter.into_iter().collect())
}
}
impl FromStr for RpcModuleSelection {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self::Selection(Default::default()));
}
let mut modules = s.split(',').map(str::trim).peekable();
let first = modules
.peek()
.copied()
.ok_or(ParseError::VariantNotFound.to_string())?;
match first {
"all" | "All" => Ok(Self::All),
"none" | "None" => Ok(Self::Selection(Default::default())),
"standard" | "Standard" => Ok(Self::Standard),
"evm" | "Evm" => Ok(Self::Evm),
_ => Self::try_from_selection(modules).map_err(|e| e.to_string()),
}
}
}
impl fmt::Display for RpcModuleSelection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}]",
self.iter_selection()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}
#[derive(
Debug,
Clone,
Copy,
Eq,
PartialEq,
Hash,
AsRefStr,
IntoStaticStr,
VariantNames,
VariantArray,
EnumIter,
Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "kebab-case")]
pub enum EthRpcModule {
Debug,
Eth,
Net,
Trace,
Web3,
Rpc,
}
impl EthRpcModule {
pub const fn variant_count() -> usize {
<Self as VariantArray>::VARIANTS.len()
}
pub const fn all_variant_names() -> &'static [&'static str] {
<Self as VariantNames>::VARIANTS
}
pub const fn all_variants() -> &'static [Self] {
<Self as VariantArray>::VARIANTS
}
pub fn modules() -> impl IntoIterator<Item = Self> {
use strum::IntoEnumIterator;
Self::iter()
}
#[inline]
pub fn as_str(&self) -> &'static str { self.into() }
}
impl FromStr for EthRpcModule {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"debug" => Self::Debug,
"eth" => Self::Eth,
"net" => Self::Net,
"trace" => Self::Trace,
"web3" => Self::Web3,
"rpc" => Self::Rpc,
_ => return Err(ParseError::VariantNotFound),
})
}
}
impl TryFrom<&str> for EthRpcModule {
type Error = ParseError;
fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
FromStr::from_str(s)
}
}
impl fmt::Display for EthRpcModule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(self.as_ref())
}
}
impl Serialize for EthRpcModule {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where S: Serializer {
s.serialize_str(self.as_ref())
}
}