cfx_rpc_builder/cfx/
module.rs

1// Copyright 2023-2024 Paradigm.xyz
2// This file is part of reth.
3// Reth is a modular, contributor-friendly and blazing-fast implementation of
4// the Ethereum protocol
5
6// Permission is hereby granted, free of charge, to any
7// person obtaining a copy of this software and associated
8// documentation files (the "Software"), to deal in the
9// Software without restriction, including without
10// limitation the rights to use, copy, modify, merge,
11// publish, distribute, sublicense, and/or sell copies of
12// the Software, and to permit persons to whom the Software
13// is furnished to do so, subject to the following
14// conditions:
15
16// The above copyright notice and this permission notice
17// shall be included in all copies or substantial portions
18// of the Software.
19
20// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
21// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
22// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
24// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
27// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28// DEALINGS IN THE SOFTWARE.
29use std::{collections::HashSet, fmt, str::FromStr};
30
31use cfx_rpc_cfx_types::apis::{Api, ApiSet};
32use serde::{Deserialize, Serialize, Serializer};
33use strum::{
34    AsRefStr, EnumIter, IntoStaticStr, ParseError, VariantArray, VariantNames,
35};
36
37#[derive(Debug, Default, Clone, Eq, PartialEq)]
38pub enum RpcModuleSelection {
39    #[default]
40    All,
41    None,
42    Standard,
43    Selection(HashSet<CfxRpcModule>),
44}
45
46impl RpcModuleSelection {
47    pub const STANDARD_MODULES: [CfxRpcModule; 2] =
48        [CfxRpcModule::Cfx, CfxRpcModule::PubSub];
49
50    pub fn all_modules() -> HashSet<CfxRpcModule> {
51        CfxRpcModule::modules().into_iter().collect()
52    }
53
54    pub fn standard_modules() -> HashSet<CfxRpcModule> {
55        HashSet::from(Self::STANDARD_MODULES)
56    }
57
58    pub fn len(&self) -> usize {
59        match self {
60            Self::All => CfxRpcModule::variant_count(),
61            Self::None => 0,
62            Self::Standard => Self::STANDARD_MODULES.len(),
63            Self::Selection(s) => s.len(),
64        }
65    }
66
67    pub fn is_empty(&self) -> bool {
68        match self {
69            Self::Selection(s) => s.is_empty(),
70            Self::None => true,
71            _ => false,
72        }
73    }
74
75    pub fn iter_selection(
76        &self,
77    ) -> Box<dyn Iterator<Item = CfxRpcModule> + '_> {
78        match self {
79            Self::All => Box::new(CfxRpcModule::modules().into_iter()),
80            Self::None => Box::new(std::iter::empty()),
81            Self::Standard => Box::new(Self::STANDARD_MODULES.iter().copied()),
82            Self::Selection(s) => Box::new(s.iter().copied()),
83        }
84    }
85
86    pub fn to_selection(&self) -> HashSet<CfxRpcModule> {
87        match self {
88            Self::All => Self::all_modules(),
89            Self::None => HashSet::new(),
90            Self::Standard => Self::standard_modules(),
91            Self::Selection(s) => s.clone(),
92        }
93    }
94
95    pub fn into_selection(self) -> HashSet<CfxRpcModule> {
96        match self {
97            Self::All => Self::all_modules(),
98            Self::None => HashSet::new(),
99            Self::Standard => Self::standard_modules(),
100            Self::Selection(s) => s,
101        }
102    }
103
104    pub fn are_identical(http: Option<&Self>, ws: Option<&Self>) -> bool {
105        match (http, ws) {
106            (Some(Self::All), Some(other)) | (Some(other), Some(Self::All)) => {
107                other.len() == CfxRpcModule::variant_count()
108            }
109            (Some(some), None) | (None, Some(some)) => some.is_empty(),
110            (Some(http), Some(ws)) => http.to_selection() == ws.to_selection(),
111            (None, None) => true,
112        }
113    }
114}
115
116impl From<&HashSet<CfxRpcModule>> for RpcModuleSelection {
117    fn from(s: &HashSet<CfxRpcModule>) -> Self { Self::from(s.clone()) }
118}
119
120impl From<HashSet<CfxRpcModule>> for RpcModuleSelection {
121    fn from(s: HashSet<CfxRpcModule>) -> Self { Self::Selection(s) }
122}
123
124impl From<&[CfxRpcModule]> for RpcModuleSelection {
125    fn from(s: &[CfxRpcModule]) -> Self {
126        Self::Selection(s.iter().copied().collect())
127    }
128}
129
130impl From<Vec<CfxRpcModule>> for RpcModuleSelection {
131    fn from(s: Vec<CfxRpcModule>) -> Self {
132        Self::Selection(s.into_iter().collect())
133    }
134}
135
136impl<const N: usize> From<[CfxRpcModule; N]> for RpcModuleSelection {
137    fn from(s: [CfxRpcModule; N]) -> Self {
138        Self::Selection(s.iter().copied().collect())
139    }
140}
141
142impl<'a> FromIterator<&'a CfxRpcModule> for RpcModuleSelection {
143    fn from_iter<I>(iter: I) -> Self
144    where I: IntoIterator<Item = &'a CfxRpcModule> {
145        iter.into_iter().copied().collect()
146    }
147}
148
149impl FromIterator<CfxRpcModule> for RpcModuleSelection {
150    fn from_iter<I>(iter: I) -> Self
151    where I: IntoIterator<Item = CfxRpcModule> {
152        Self::Selection(iter.into_iter().collect())
153    }
154}
155
156impl FromStr for RpcModuleSelection {
157    type Err = String;
158
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        if s.is_empty() {
161            return Ok(Self::Selection(Default::default()));
162        }
163        let mut modules = s.split(',').map(str::trim).peekable();
164        let first = modules
165            .peek()
166            .copied()
167            .ok_or(ParseError::VariantNotFound.to_string())?;
168        match first {
169            "all" | "All" => Ok(Self::All),
170            "none" | "None" => Ok(Self::None),
171            "standard" | "Standard" => Ok(Self::Standard),
172            "safe" => {
173                let mut selection = HashSet::new();
174                selection.insert(CfxRpcModule::Cfx);
175                selection.insert(CfxRpcModule::PubSub);
176                selection.insert(CfxRpcModule::Txpool);
177                Ok(Self::Selection(selection))
178            }
179            _ => {
180                let mut selection = HashSet::new();
181                for module in modules {
182                    let m: CfxRpcModule = module
183                        .parse()
184                        .map_err(|e: ParseError| e.to_string())?;
185                    selection.insert(m);
186                }
187                Ok(Self::Selection(selection))
188            }
189        }
190    }
191}
192
193impl fmt::Display for RpcModuleSelection {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        write!(
196            f,
197            "[{}]",
198            self.iter_selection()
199                .map(|s| s.to_string())
200                .collect::<Vec<_>>()
201                .join(", ")
202        )
203    }
204}
205
206impl From<Api> for CfxRpcModule {
207    fn from(value: Api) -> Self {
208        match value {
209            Api::Cfx => Self::Cfx,
210            Api::Debug => Self::Debug,
211            Api::Pubsub => Self::PubSub,
212            Api::Test => Self::Test,
213            Api::Trace => Self::Trace,
214            Api::TxPool => Self::Txpool,
215            Api::Pos => Self::Pos,
216        }
217    }
218}
219
220impl From<CfxRpcModule> for Api {
221    fn from(value: CfxRpcModule) -> Self {
222        match value {
223            CfxRpcModule::Cfx => Self::Cfx,
224            CfxRpcModule::Debug => Self::Debug,
225            CfxRpcModule::Pos => Self::Pos,
226            CfxRpcModule::Trace => Self::Trace,
227            CfxRpcModule::Txpool => Self::TxPool,
228            CfxRpcModule::Test => Self::Test,
229            CfxRpcModule::PubSub => Self::Pubsub,
230        }
231    }
232}
233
234impl From<ApiSet> for RpcModuleSelection {
235    fn from(value: ApiSet) -> Self {
236        Self::Selection(value.list_apis().into_iter().map(Into::into).collect())
237    }
238}
239
240impl From<RpcModuleSelection> for ApiSet {
241    fn from(value: RpcModuleSelection) -> Self {
242        ApiSet::List(
243            value.into_selection().into_iter().map(Into::into).collect(),
244        )
245    }
246}
247
248#[derive(
249    Debug,
250    Clone,
251    Copy,
252    Eq,
253    PartialEq,
254    Hash,
255    AsRefStr,
256    IntoStaticStr,
257    VariantNames,
258    VariantArray,
259    EnumIter,
260    Deserialize,
261)]
262#[serde(rename_all = "snake_case")]
263#[strum(serialize_all = "kebab-case")]
264pub enum CfxRpcModule {
265    Cfx,
266    Debug,
267    Pos,
268    Trace,
269    Txpool,
270    Test,
271    PubSub,
272}
273
274impl CfxRpcModule {
275    pub const fn variant_count() -> usize {
276        <Self as VariantArray>::VARIANTS.len()
277    }
278
279    pub const fn all_variant_names() -> &'static [&'static str] {
280        <Self as VariantNames>::VARIANTS
281    }
282
283    pub const fn all_variants() -> &'static [Self] {
284        <Self as VariantArray>::VARIANTS
285    }
286
287    pub fn modules() -> impl IntoIterator<Item = Self> {
288        use strum::IntoEnumIterator;
289        Self::iter()
290    }
291
292    #[inline]
293    pub fn as_str(&self) -> &'static str { self.into() }
294}
295
296impl FromStr for CfxRpcModule {
297    type Err = ParseError;
298
299    fn from_str(s: &str) -> Result<Self, Self::Err> {
300        Ok(match s {
301            "cfx" => Self::Cfx,
302            "debug" => Self::Debug,
303            "pos" => Self::Pos,
304            "trace" => Self::Trace,
305            "txpool" => Self::Txpool,
306            "test" => Self::Test,
307            "pubsub" => Self::PubSub,
308            _ => return Err(ParseError::VariantNotFound),
309        })
310    }
311}
312
313impl TryFrom<&str> for CfxRpcModule {
314    type Error = ParseError;
315
316    fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
317        FromStr::from_str(s)
318    }
319}
320
321impl fmt::Display for CfxRpcModule {
322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323        f.pad(self.as_ref())
324    }
325}
326
327impl Serialize for CfxRpcModule {
328    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
329    where S: Serializer {
330        s.serialize_str(self.as_ref())
331    }
332}