1use 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}