1use std::{collections::HashSet, fmt, str::FromStr};
2
3use serde::{Deserialize, Serialize, Serializer};
4use strum::{AsRefStr, EnumIter, IntoStaticStr, ParseError, VariantArray, VariantNames};
5
6#[derive(Debug, Default, Clone, Eq, PartialEq)]
17pub enum RpcModuleSelection {
18 All,
20 #[default]
22 Standard,
23 Selection(HashSet<RethRpcModule>),
25}
26
27impl RpcModuleSelection {
30 pub const STANDARD_MODULES: [RethRpcModule; 3] =
32 [RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3];
33
34 pub fn all_modules() -> HashSet<RethRpcModule> {
36 RethRpcModule::modules().into_iter().collect()
37 }
38
39 pub fn standard_modules() -> HashSet<RethRpcModule> {
41 HashSet::from(Self::STANDARD_MODULES)
42 }
43
44 pub fn default_ipc_modules() -> HashSet<RethRpcModule> {
48 Self::all_modules()
49 }
50
51 pub fn try_from_selection<I, T>(selection: I) -> Result<Self, T::Error>
77 where
78 I: IntoIterator<Item = T>,
79 T: TryInto<RethRpcModule>,
80 {
81 selection.into_iter().map(TryInto::try_into).collect()
82 }
83
84 pub fn len(&self) -> usize {
86 match self {
87 Self::All => RethRpcModule::variant_count(),
88 Self::Standard => Self::STANDARD_MODULES.len(),
89 Self::Selection(s) => s.len(),
90 }
91 }
92
93 pub fn is_empty(&self) -> bool {
95 match self {
96 Self::Selection(sel) => sel.is_empty(),
97 _ => false,
98 }
99 }
100
101 pub fn iter_selection(&self) -> Box<dyn Iterator<Item = RethRpcModule> + '_> {
103 match self {
104 Self::All => Box::new(RethRpcModule::modules().into_iter()),
105 Self::Standard => Box::new(Self::STANDARD_MODULES.iter().copied()),
106 Self::Selection(s) => Box::new(s.iter().copied()),
107 }
108 }
109
110 pub fn to_selection(&self) -> HashSet<RethRpcModule> {
112 match self {
113 Self::All => Self::all_modules(),
114 Self::Standard => Self::standard_modules(),
115 Self::Selection(s) => s.clone(),
116 }
117 }
118
119 pub fn into_selection(self) -> HashSet<RethRpcModule> {
121 match self {
122 Self::All => Self::all_modules(),
123 Self::Standard => Self::standard_modules(),
124 Self::Selection(s) => s,
125 }
126 }
127
128 pub fn are_identical(http: Option<&Self>, ws: Option<&Self>) -> bool {
130 match (http, ws) {
131 (Some(Self::All), Some(other)) | (Some(other), Some(Self::All)) => {
133 other.len() == RethRpcModule::variant_count()
134 }
135
136 (Some(some), None) | (None, Some(some)) => some.is_empty(),
138
139 (Some(http), Some(ws)) => http.to_selection() == ws.to_selection(),
140 (None, None) => true,
141 }
142 }
143
144 pub fn contains(&self, module: &RethRpcModule) -> bool {
146 match self {
147 Self::All => true,
148 Self::Standard => Self::STANDARD_MODULES.contains(module),
149 Self::Selection(s) => s.contains(module),
150 }
151 }
152}
153
154impl From<&HashSet<RethRpcModule>> for RpcModuleSelection {
155 fn from(s: &HashSet<RethRpcModule>) -> Self {
156 Self::from(s.clone())
157 }
158}
159
160impl From<HashSet<RethRpcModule>> for RpcModuleSelection {
161 fn from(s: HashSet<RethRpcModule>) -> Self {
162 Self::Selection(s)
163 }
164}
165
166impl From<&[RethRpcModule]> for RpcModuleSelection {
167 fn from(s: &[RethRpcModule]) -> Self {
168 Self::Selection(s.iter().copied().collect())
169 }
170}
171
172impl From<Vec<RethRpcModule>> for RpcModuleSelection {
173 fn from(s: Vec<RethRpcModule>) -> Self {
174 Self::Selection(s.into_iter().collect())
175 }
176}
177
178impl<const N: usize> From<[RethRpcModule; N]> for RpcModuleSelection {
179 fn from(s: [RethRpcModule; N]) -> Self {
180 Self::Selection(s.iter().copied().collect())
181 }
182}
183
184impl<'a> FromIterator<&'a RethRpcModule> for RpcModuleSelection {
185 fn from_iter<I>(iter: I) -> Self
186 where
187 I: IntoIterator<Item = &'a RethRpcModule>,
188 {
189 iter.into_iter().copied().collect()
190 }
191}
192
193impl FromIterator<RethRpcModule> for RpcModuleSelection {
194 fn from_iter<I>(iter: I) -> Self
195 where
196 I: IntoIterator<Item = RethRpcModule>,
197 {
198 Self::Selection(iter.into_iter().collect())
199 }
200}
201
202impl FromStr for RpcModuleSelection {
203 type Err = ParseError;
204
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
206 if s.is_empty() {
207 return Ok(Self::Selection(Default::default()))
208 }
209 let mut modules = s.split(',').map(str::trim).peekable();
210 let first = modules.peek().copied().ok_or(ParseError::VariantNotFound)?;
211 match first.to_lowercase().as_str() {
215 "all" => Ok(Self::All),
216 "none" => Ok(Self::Selection(Default::default())),
217 _ => Self::try_from_selection(modules),
218 }
219 }
220}
221
222impl fmt::Display for RpcModuleSelection {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(
225 f,
226 "[{}]",
227 self.iter_selection().map(|s| s.to_string()).collect::<Vec<_>>().join(", ")
228 )
229 }
230}
231
232#[derive(
234 Debug,
235 Clone,
236 Copy,
237 Eq,
238 PartialEq,
239 Hash,
240 AsRefStr,
241 IntoStaticStr,
242 VariantNames,
243 VariantArray,
244 EnumIter,
245 Deserialize,
246)]
247#[serde(rename_all = "snake_case")]
248#[strum(serialize_all = "kebab-case")]
249pub enum RethRpcModule {
250 Admin,
252 Debug,
254 Eth,
256 Net,
258 Trace,
260 Txpool,
262 Web3,
264 Rpc,
266 Reth,
268 Ots,
270 Flashbots,
272 Miner,
274}
275
276impl RethRpcModule {
279 pub const fn variant_count() -> usize {
281 <Self as VariantArray>::VARIANTS.len()
282 }
283
284 pub const fn all_variant_names() -> &'static [&'static str] {
286 <Self as VariantNames>::VARIANTS
287 }
288
289 pub const fn all_variants() -> &'static [Self] {
291 <Self as VariantArray>::VARIANTS
292 }
293
294 pub fn modules() -> impl IntoIterator<Item = Self> {
296 use strum::IntoEnumIterator;
297 Self::iter()
298 }
299
300 #[inline]
302 pub fn as_str(&self) -> &'static str {
303 self.into()
304 }
305}
306
307impl FromStr for RethRpcModule {
308 type Err = ParseError;
309
310 fn from_str(s: &str) -> Result<Self, Self::Err> {
311 Ok(match s {
312 "admin" => Self::Admin,
313 "debug" => Self::Debug,
314 "eth" => Self::Eth,
315 "net" => Self::Net,
316 "trace" => Self::Trace,
317 "txpool" => Self::Txpool,
318 "web3" => Self::Web3,
319 "rpc" => Self::Rpc,
320 "reth" => Self::Reth,
321 "ots" => Self::Ots,
322 "flashbots" => Self::Flashbots,
323 "miner" => Self::Miner,
324 _ => return Err(ParseError::VariantNotFound),
325 })
326 }
327}
328
329impl TryFrom<&str> for RethRpcModule {
330 type Error = ParseError;
331 fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
332 FromStr::from_str(s)
333 }
334}
335
336impl fmt::Display for RethRpcModule {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 f.pad(self.as_ref())
339 }
340}
341
342impl Serialize for RethRpcModule {
343 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
344 where
345 S: Serializer,
346 {
347 s.serialize_str(self.as_ref())
348 }
349}
350
351#[cfg(test)]
352mod test {
353 use super::*;
354
355 #[test]
356 fn test_all_modules() {
357 let all_modules = RpcModuleSelection::all_modules();
358 assert_eq!(all_modules.len(), RethRpcModule::variant_count());
359 }
360
361 #[test]
362 fn test_standard_modules() {
363 let standard_modules = RpcModuleSelection::standard_modules();
364 let expected_modules: HashSet<RethRpcModule> =
365 HashSet::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
366 assert_eq!(standard_modules, expected_modules);
367 }
368
369 #[test]
370 fn test_default_ipc_modules() {
371 let default_ipc_modules = RpcModuleSelection::default_ipc_modules();
372 assert_eq!(default_ipc_modules, RpcModuleSelection::all_modules());
373 }
374
375 #[test]
376 fn test_try_from_selection_success() {
377 let selection = vec!["eth", "admin"];
378 let config = RpcModuleSelection::try_from_selection(selection).unwrap();
379 assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
380 }
381
382 #[test]
383 fn test_rpc_module_selection_len() {
384 let all_modules = RpcModuleSelection::All;
385 let standard = RpcModuleSelection::Standard;
386 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
387
388 assert_eq!(all_modules.len(), RethRpcModule::variant_count());
389 assert_eq!(standard.len(), 3);
390 assert_eq!(selection.len(), 2);
391 }
392
393 #[test]
394 fn test_rpc_module_selection_is_empty() {
395 let empty_selection = RpcModuleSelection::from(HashSet::new());
396 assert!(empty_selection.is_empty());
397
398 let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
399 assert!(!non_empty_selection.is_empty());
400 }
401
402 #[test]
403 fn test_rpc_module_selection_iter_selection() {
404 let all_modules = RpcModuleSelection::All;
405 let standard = RpcModuleSelection::Standard;
406 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
407
408 assert_eq!(all_modules.iter_selection().count(), RethRpcModule::variant_count());
409 assert_eq!(standard.iter_selection().count(), 3);
410 assert_eq!(selection.iter_selection().count(), 2);
411 }
412
413 #[test]
414 fn test_rpc_module_selection_to_selection() {
415 let all_modules = RpcModuleSelection::All;
416 let standard = RpcModuleSelection::Standard;
417 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
418
419 assert_eq!(all_modules.to_selection(), RpcModuleSelection::all_modules());
420 assert_eq!(standard.to_selection(), RpcModuleSelection::standard_modules());
421 assert_eq!(
422 selection.to_selection(),
423 HashSet::from([RethRpcModule::Eth, RethRpcModule::Admin])
424 );
425 }
426
427 #[test]
428 fn test_rpc_module_selection_are_identical() {
429 let all_modules = RpcModuleSelection::All;
434 assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&all_modules)));
435
436 assert!(RpcModuleSelection::are_identical(None, None));
441
442 let selection1 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
447 let selection2 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
448 assert!(RpcModuleSelection::are_identical(Some(&selection1), Some(&selection2)));
449
450 let standard = RpcModuleSelection::Standard;
456 assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&standard)));
457
458 let empty_selection = RpcModuleSelection::Selection(HashSet::new());
463 assert!(RpcModuleSelection::are_identical(None, Some(&empty_selection)));
464 assert!(RpcModuleSelection::are_identical(Some(&empty_selection), None));
465
466 let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
472 assert!(!RpcModuleSelection::are_identical(None, Some(&non_empty_selection)));
473 assert!(!RpcModuleSelection::are_identical(Some(&non_empty_selection), None));
474
475 let partial_selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
480 assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&partial_selection)));
481
482 let full_selection =
487 RpcModuleSelection::from(RethRpcModule::modules().into_iter().collect::<HashSet<_>>());
488 assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&full_selection)));
489
490 let selection3 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
495 let selection4 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Web3]);
496 assert!(!RpcModuleSelection::are_identical(Some(&selection3), Some(&selection4)));
497
498 let matching_standard =
502 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
503 assert!(RpcModuleSelection::are_identical(Some(&standard), Some(&matching_standard)));
504
505 let non_matching_standard =
510 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
511 assert!(!RpcModuleSelection::are_identical(Some(&standard), Some(&non_matching_standard)));
512 }
513
514 #[test]
515 fn test_rpc_module_selection_from_str() {
516 let result = RpcModuleSelection::from_str("");
518 assert!(result.is_ok());
519 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
520
521 let result = RpcModuleSelection::from_str("all");
523 assert!(result.is_ok());
524 assert_eq!(result.unwrap(), RpcModuleSelection::All);
525
526 let result = RpcModuleSelection::from_str("All");
527 assert!(result.is_ok());
528 assert_eq!(result.unwrap(), RpcModuleSelection::All);
529
530 let result = RpcModuleSelection::from_str("ALL");
531 assert!(result.is_ok());
532 assert_eq!(result.unwrap(), RpcModuleSelection::All);
533
534 let result = RpcModuleSelection::from_str("none");
536 assert!(result.is_ok());
537 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
538
539 let result = RpcModuleSelection::from_str("None");
540 assert!(result.is_ok());
541 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
542
543 let result = RpcModuleSelection::from_str("NONE");
544 assert!(result.is_ok());
545 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
546
547 let result = RpcModuleSelection::from_str("eth,admin");
549 assert!(result.is_ok());
550 let expected_selection =
551 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
552 assert_eq!(result.unwrap(), expected_selection);
553
554 let result = RpcModuleSelection::from_str(" eth , admin ");
556 assert!(result.is_ok());
557 assert_eq!(result.unwrap(), expected_selection);
558
559 let result = RpcModuleSelection::from_str("invalid,unknown");
561 assert!(result.is_err());
562 assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
563
564 let result = RpcModuleSelection::from_str("eth");
566 assert!(result.is_ok());
567 let expected_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
568 assert_eq!(result.unwrap(), expected_selection);
569
570 let result = RpcModuleSelection::from_str("unknown");
572 assert!(result.is_err());
573 assert_eq!(result.unwrap_err(), ParseError::VariantNotFound);
574 }
575}