1use std::{collections::HashSet, fmt, str::FromStr};
2
3use serde::{Deserialize, Serialize, Serializer};
4use strum::{ParseError, 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 const fn is_all(&self) -> bool {
103 matches!(self, Self::All)
104 }
105
106 pub fn iter_selection(&self) -> Box<dyn Iterator<Item = RethRpcModule> + '_> {
108 match self {
109 Self::All => Box::new(RethRpcModule::modules().into_iter()),
110 Self::Standard => Box::new(Self::STANDARD_MODULES.iter().cloned()),
111 Self::Selection(s) => Box::new(s.iter().cloned()),
112 }
113 }
114
115 pub fn to_selection(&self) -> HashSet<RethRpcModule> {
117 match self {
118 Self::All => Self::all_modules(),
119 Self::Standard => Self::standard_modules(),
120 Self::Selection(s) => s.clone(),
121 }
122 }
123
124 pub fn into_selection(self) -> HashSet<RethRpcModule> {
126 match self {
127 Self::All => Self::all_modules(),
128 Self::Standard => Self::standard_modules(),
129 Self::Selection(s) => s,
130 }
131 }
132
133 pub fn are_identical(http: Option<&Self>, ws: Option<&Self>) -> bool {
135 match (http, ws) {
136 (Some(Self::All), Some(other)) | (Some(other), Some(Self::All)) => {
138 other.len() == RethRpcModule::variant_count()
139 }
140
141 (Some(some), None) | (None, Some(some)) => some.is_empty(),
143
144 (Some(http), Some(ws)) => http.to_selection() == ws.to_selection(),
145 (None, None) => true,
146 }
147 }
148
149 pub fn contains(&self, module: &RethRpcModule) -> bool {
151 match self {
152 Self::All => true,
153 Self::Standard => Self::STANDARD_MODULES.contains(module),
154 Self::Selection(s) => s.contains(module),
155 }
156 }
157
158 pub fn push(&mut self, module: RethRpcModule) {
163 if !self.is_all() {
164 let mut modules = self.to_selection();
165 modules.insert(module);
166 *self = Self::Selection(modules);
167 }
168 }
169
170 pub fn append(self, module: RethRpcModule) -> Self {
175 if self.is_all() {
176 Self::All
177 } else {
178 let mut modules = self.into_selection();
179 modules.insert(module);
180 Self::Selection(modules)
181 }
182 }
183
184 pub fn extend<I>(&mut self, iter: I)
189 where
190 I: IntoIterator<Item = RethRpcModule>,
191 {
192 if !self.is_all() {
193 let mut modules = self.to_selection();
194 modules.extend(iter);
195 *self = Self::Selection(modules);
196 }
197 }
198
199 pub fn extended<I>(self, iter: I) -> Self
204 where
205 I: IntoIterator<Item = RethRpcModule>,
206 {
207 if self.is_all() {
208 Self::All
209 } else {
210 let mut modules = self.into_selection();
211 modules.extend(iter);
212 Self::Selection(modules)
213 }
214 }
215}
216
217impl From<&HashSet<RethRpcModule>> for RpcModuleSelection {
218 fn from(s: &HashSet<RethRpcModule>) -> Self {
219 Self::from(s.clone())
220 }
221}
222
223impl From<HashSet<RethRpcModule>> for RpcModuleSelection {
224 fn from(s: HashSet<RethRpcModule>) -> Self {
225 Self::Selection(s)
226 }
227}
228
229impl From<&[RethRpcModule]> for RpcModuleSelection {
230 fn from(s: &[RethRpcModule]) -> Self {
231 Self::Selection(s.iter().cloned().collect())
232 }
233}
234
235impl From<Vec<RethRpcModule>> for RpcModuleSelection {
236 fn from(s: Vec<RethRpcModule>) -> Self {
237 Self::Selection(s.into_iter().collect())
238 }
239}
240
241impl<const N: usize> From<[RethRpcModule; N]> for RpcModuleSelection {
242 fn from(s: [RethRpcModule; N]) -> Self {
243 Self::Selection(s.into_iter().collect())
244 }
245}
246
247impl<'a> FromIterator<&'a RethRpcModule> for RpcModuleSelection {
248 fn from_iter<I>(iter: I) -> Self
249 where
250 I: IntoIterator<Item = &'a RethRpcModule>,
251 {
252 iter.into_iter().cloned().collect()
253 }
254}
255
256impl FromIterator<RethRpcModule> for RpcModuleSelection {
257 fn from_iter<I>(iter: I) -> Self
258 where
259 I: IntoIterator<Item = RethRpcModule>,
260 {
261 Self::Selection(iter.into_iter().collect())
262 }
263}
264
265impl FromStr for RpcModuleSelection {
266 type Err = ParseError;
267
268 fn from_str(s: &str) -> Result<Self, Self::Err> {
269 if s.is_empty() {
270 return Ok(Self::Selection(Default::default()))
271 }
272 let mut modules = s.split(',').map(str::trim).peekable();
273 let first = modules.peek().copied().ok_or(ParseError::VariantNotFound)?;
274 match first.to_lowercase().as_str() {
278 "all" => Ok(Self::All),
279 "none" => Ok(Self::Selection(Default::default())),
280 _ => Self::try_from_selection(modules),
281 }
282 }
283}
284
285impl fmt::Display for RpcModuleSelection {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 write!(
288 f,
289 "[{}]",
290 self.iter_selection().map(|s| s.to_string()).collect::<Vec<_>>().join(", ")
291 )
292 }
293}
294
295#[derive(Debug, Clone, Eq, PartialEq, Hash, VariantNames, Deserialize)]
297#[serde(rename_all = "snake_case")]
298#[strum(serialize_all = "kebab-case")]
299pub enum RethRpcModule {
300 Admin,
302 Debug,
304 Eth,
306 Net,
308 Trace,
310 Txpool,
312 Web3,
314 Rpc,
316 Reth,
318 Ots,
320 Flashbots,
322 Miner,
324 Mev,
326 Testing,
328 #[strum(default)]
330 #[serde(untagged)]
331 Other(String),
332}
333
334impl RethRpcModule {
337 const STANDARD_VARIANTS: &'static [Self] = &[
339 Self::Admin,
340 Self::Debug,
341 Self::Eth,
342 Self::Net,
343 Self::Trace,
344 Self::Txpool,
345 Self::Web3,
346 Self::Rpc,
347 Self::Reth,
348 Self::Ots,
349 Self::Flashbots,
350 Self::Miner,
351 Self::Mev,
352 Self::Testing,
353 ];
354
355 pub const fn variant_count() -> usize {
357 Self::STANDARD_VARIANTS.len()
358 }
359
360 pub const fn all_variant_names() -> &'static [&'static str] {
362 <Self as VariantNames>::VARIANTS
363 }
364
365 pub fn standard_variant_names() -> impl Iterator<Item = &'static str> {
367 <Self as VariantNames>::VARIANTS.iter().copied().filter(|&name| name != "other")
368 }
369
370 pub const fn all_variants() -> &'static [Self] {
372 Self::STANDARD_VARIANTS
373 }
374
375 pub fn modules() -> impl IntoIterator<Item = Self> + Clone {
377 Self::STANDARD_VARIANTS.iter().cloned()
378 }
379
380 pub fn as_str(&self) -> &str {
382 match self {
383 Self::Other(s) => s.as_str(),
384 _ => self.as_ref(), }
386 }
387
388 pub const fn is_other(&self) -> bool {
390 matches!(self, Self::Other(_))
391 }
392}
393
394impl AsRef<str> for RethRpcModule {
395 fn as_ref(&self) -> &str {
396 match self {
397 Self::Other(s) => s.as_str(),
398 Self::Admin => "admin",
400 Self::Debug => "debug",
401 Self::Eth => "eth",
402 Self::Net => "net",
403 Self::Trace => "trace",
404 Self::Txpool => "txpool",
405 Self::Web3 => "web3",
406 Self::Rpc => "rpc",
407 Self::Reth => "reth",
408 Self::Ots => "ots",
409 Self::Flashbots => "flashbots",
410 Self::Miner => "miner",
411 Self::Mev => "mev",
412 Self::Testing => "testing",
413 }
414 }
415}
416
417impl FromStr for RethRpcModule {
418 type Err = ParseError;
419
420 fn from_str(s: &str) -> Result<Self, Self::Err> {
421 Ok(match s {
422 "admin" => Self::Admin,
423 "debug" => Self::Debug,
424 "eth" => Self::Eth,
425 "net" => Self::Net,
426 "trace" => Self::Trace,
427 "txpool" => Self::Txpool,
428 "web3" => Self::Web3,
429 "rpc" => Self::Rpc,
430 "reth" => Self::Reth,
431 "ots" => Self::Ots,
432 "flashbots" => Self::Flashbots,
433 "miner" => Self::Miner,
434 "mev" => Self::Mev,
435 "testing" => Self::Testing,
436 other => Self::Other(other.to_string()),
438 })
439 }
440}
441
442impl TryFrom<&str> for RethRpcModule {
443 type Error = ParseError;
444 fn try_from(s: &str) -> Result<Self, <Self as TryFrom<&str>>::Error> {
445 FromStr::from_str(s)
446 }
447}
448
449impl fmt::Display for RethRpcModule {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 f.pad(self.as_ref())
452 }
453}
454
455impl Serialize for RethRpcModule {
456 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
457 where
458 S: Serializer,
459 {
460 s.serialize_str(self.as_str())
461 }
462}
463
464pub trait RpcModuleValidator: Clone + Send + Sync + 'static {
469 fn parse_selection(s: &str) -> Result<RpcModuleSelection, String>;
471
472 fn validate_selection(modules: &RpcModuleSelection, arg_name: &str) -> Result<(), String> {
477 let RpcModuleSelection::Selection(module_set) = modules else {
481 return Ok(());
483 };
484
485 for module in module_set {
486 let RethRpcModule::Other(name) = module else {
487 continue;
489 };
490
491 Self::parse_selection(name)
494 .map_err(|e| format!("Invalid RPC module '{name}' in {arg_name}: {e}"))?;
495 }
496
497 Ok(())
498 }
499}
500
501#[derive(Debug, Clone, Copy)]
505pub struct DefaultRpcModuleValidator;
506
507impl RpcModuleValidator for DefaultRpcModuleValidator {
508 fn parse_selection(s: &str) -> Result<RpcModuleSelection, String> {
509 let selection = RpcModuleSelection::from_str(s)
511 .map_err(|e| format!("Failed to parse RPC modules: {}", e))?;
512
513 if let RpcModuleSelection::Selection(modules) = &selection {
515 for module in modules {
516 if let RethRpcModule::Other(name) = module {
517 return Err(format!("Unknown RPC module: '{}'", name));
518 }
519 }
520 }
521
522 Ok(selection)
523 }
524}
525
526#[derive(Debug, Clone, Copy)]
530pub struct LenientRpcModuleValidator;
531
532impl RpcModuleValidator for LenientRpcModuleValidator {
533 fn parse_selection(s: &str) -> Result<RpcModuleSelection, String> {
534 RpcModuleSelection::from_str(s).map_err(|e| format!("Failed to parse RPC modules: {}", e))
535 }
536}
537
538#[cfg(test)]
539mod test {
540 use super::*;
541
542 #[test]
543 fn test_all_modules() {
544 let all_modules = RpcModuleSelection::all_modules();
545 assert_eq!(all_modules.len(), RethRpcModule::variant_count());
546 }
547
548 #[test]
549 fn test_standard_modules() {
550 let standard_modules = RpcModuleSelection::standard_modules();
551 let expected_modules: HashSet<RethRpcModule> =
552 HashSet::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
553 assert_eq!(standard_modules, expected_modules);
554 }
555
556 #[test]
557 fn test_default_ipc_modules() {
558 let default_ipc_modules = RpcModuleSelection::default_ipc_modules();
559 assert_eq!(default_ipc_modules, RpcModuleSelection::all_modules());
560 }
561
562 #[test]
563 fn test_try_from_selection_success() {
564 let selection = vec!["eth", "admin"];
565 let config = RpcModuleSelection::try_from_selection(selection).unwrap();
566 assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]));
567 }
568
569 #[test]
570 fn test_rpc_module_selection_len() {
571 let all_modules = RpcModuleSelection::All;
572 let standard = RpcModuleSelection::Standard;
573 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
574
575 assert_eq!(all_modules.len(), RethRpcModule::variant_count());
576 assert_eq!(standard.len(), 3);
577 assert_eq!(selection.len(), 2);
578 }
579
580 #[test]
581 fn test_rpc_module_selection_is_empty() {
582 let empty_selection = RpcModuleSelection::from(HashSet::new());
583 assert!(empty_selection.is_empty());
584
585 let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
586 assert!(!non_empty_selection.is_empty());
587 }
588
589 #[test]
590 fn test_rpc_module_selection_iter_selection() {
591 let all_modules = RpcModuleSelection::All;
592 let standard = RpcModuleSelection::Standard;
593 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
594
595 assert_eq!(all_modules.iter_selection().count(), RethRpcModule::variant_count());
596 assert_eq!(standard.iter_selection().count(), 3);
597 assert_eq!(selection.iter_selection().count(), 2);
598 }
599
600 #[test]
601 fn test_rpc_module_selection_to_selection() {
602 let all_modules = RpcModuleSelection::All;
603 let standard = RpcModuleSelection::Standard;
604 let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
605
606 assert_eq!(all_modules.to_selection(), RpcModuleSelection::all_modules());
607 assert_eq!(standard.to_selection(), RpcModuleSelection::standard_modules());
608 assert_eq!(
609 selection.to_selection(),
610 HashSet::from([RethRpcModule::Eth, RethRpcModule::Admin])
611 );
612 }
613
614 #[test]
615 fn test_rpc_module_selection_are_identical() {
616 let all_modules = RpcModuleSelection::All;
621 assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&all_modules)));
622
623 assert!(RpcModuleSelection::are_identical(None, None));
628
629 let selection1 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
634 let selection2 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
635 assert!(RpcModuleSelection::are_identical(Some(&selection1), Some(&selection2)));
636
637 let standard = RpcModuleSelection::Standard;
643 assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&standard)));
644
645 let empty_selection = RpcModuleSelection::Selection(HashSet::new());
650 assert!(RpcModuleSelection::are_identical(None, Some(&empty_selection)));
651 assert!(RpcModuleSelection::are_identical(Some(&empty_selection), None));
652
653 let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
659 assert!(!RpcModuleSelection::are_identical(None, Some(&non_empty_selection)));
660 assert!(!RpcModuleSelection::are_identical(Some(&non_empty_selection), None));
661
662 let partial_selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
667 assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&partial_selection)));
668
669 let full_selection =
674 RpcModuleSelection::from(RethRpcModule::modules().into_iter().collect::<HashSet<_>>());
675 assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&full_selection)));
676
677 let selection3 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
682 let selection4 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Web3]);
683 assert!(!RpcModuleSelection::are_identical(Some(&selection3), Some(&selection4)));
684
685 let matching_standard =
689 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]);
690 assert!(RpcModuleSelection::are_identical(Some(&standard), Some(&matching_standard)));
691
692 let non_matching_standard =
697 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]);
698 assert!(!RpcModuleSelection::are_identical(Some(&standard), Some(&non_matching_standard)));
699 }
700
701 #[test]
702 fn test_rpc_module_selection_append() {
703 let selection = RpcModuleSelection::Standard;
705 let new_selection = selection.append(RethRpcModule::Admin);
706 assert!(new_selection.contains(&RethRpcModule::Eth));
707 assert!(new_selection.contains(&RethRpcModule::Net));
708 assert!(new_selection.contains(&RethRpcModule::Web3));
709 assert!(new_selection.contains(&RethRpcModule::Admin));
710
711 let selection = RpcModuleSelection::Selection(HashSet::new());
713 let new_selection = selection.append(RethRpcModule::Eth);
714 assert!(new_selection.contains(&RethRpcModule::Eth));
715 assert_eq!(new_selection.len(), 1);
716
717 let selection = RpcModuleSelection::All;
719 let new_selection = selection.append(RethRpcModule::Eth);
720 assert_eq!(new_selection, RpcModuleSelection::All);
721 }
722
723 #[test]
724 fn test_rpc_module_selection_extend() {
725 let mut selection = RpcModuleSelection::Standard;
727 selection.extend(vec![RethRpcModule::Admin, RethRpcModule::Debug]);
728 assert!(selection.contains(&RethRpcModule::Eth));
729 assert!(selection.contains(&RethRpcModule::Net));
730 assert!(selection.contains(&RethRpcModule::Web3));
731 assert!(selection.contains(&RethRpcModule::Admin));
732 assert!(selection.contains(&RethRpcModule::Debug));
733
734 let mut selection = RpcModuleSelection::Selection(HashSet::new());
736 selection.extend(vec![RethRpcModule::Eth, RethRpcModule::Admin]);
737 assert!(selection.contains(&RethRpcModule::Eth));
738 assert!(selection.contains(&RethRpcModule::Admin));
739 assert_eq!(selection.len(), 2);
740
741 let mut selection = RpcModuleSelection::All;
743 selection.extend(vec![RethRpcModule::Eth, RethRpcModule::Admin]);
744 assert_eq!(selection, RpcModuleSelection::All);
745 }
746
747 #[test]
748 fn test_rpc_module_selection_from_str() {
749 let result = RpcModuleSelection::from_str("");
751 assert!(result.is_ok());
752 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
753
754 let result = RpcModuleSelection::from_str("all");
756 assert!(result.is_ok());
757 assert_eq!(result.unwrap(), RpcModuleSelection::All);
758
759 let result = RpcModuleSelection::from_str("All");
760 assert!(result.is_ok());
761 assert_eq!(result.unwrap(), RpcModuleSelection::All);
762
763 let result = RpcModuleSelection::from_str("ALL");
764 assert!(result.is_ok());
765 assert_eq!(result.unwrap(), RpcModuleSelection::All);
766
767 let result = RpcModuleSelection::from_str("none");
769 assert!(result.is_ok());
770 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
771
772 let result = RpcModuleSelection::from_str("None");
773 assert!(result.is_ok());
774 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
775
776 let result = RpcModuleSelection::from_str("NONE");
777 assert!(result.is_ok());
778 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
779
780 let result = RpcModuleSelection::from_str("eth,admin");
782 assert!(result.is_ok());
783 let expected_selection =
784 RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]);
785 assert_eq!(result.unwrap(), expected_selection);
786
787 let result = RpcModuleSelection::from_str(" eth , admin ");
789 assert!(result.is_ok());
790 assert_eq!(result.unwrap(), expected_selection);
791
792 let result = RpcModuleSelection::from_str("invalid,unknown");
794 assert!(result.is_ok());
795 let selection = result.unwrap();
796 assert!(selection.contains(&RethRpcModule::Other("invalid".to_string())));
797 assert!(selection.contains(&RethRpcModule::Other("unknown".to_string())));
798
799 let result = RpcModuleSelection::from_str("eth");
801 assert!(result.is_ok());
802 let expected_selection = RpcModuleSelection::from([RethRpcModule::Eth]);
803 assert_eq!(result.unwrap(), expected_selection);
804
805 let result = RpcModuleSelection::from_str("unknown");
807 assert!(result.is_ok());
808 let expected_selection =
809 RpcModuleSelection::from([RethRpcModule::Other("unknown".to_string())]);
810 assert_eq!(result.unwrap(), expected_selection);
811 }
812
813 #[test]
814 fn test_rpc_module_other_variant() {
815 let custom_module = RethRpcModule::from_str("myCustomModule").unwrap();
817 assert_eq!(custom_module, RethRpcModule::Other("myCustomModule".to_string()));
818
819 assert_eq!(custom_module.as_str(), "myCustomModule");
821
822 assert_eq!(custom_module.as_ref(), "myCustomModule");
824
825 assert_eq!(custom_module.to_string(), "myCustomModule");
827 }
828
829 #[test]
830 fn test_rpc_module_selection_with_mixed_modules() {
831 let result = RpcModuleSelection::from_str("eth,admin,myCustomModule,anotherCustom");
833 assert!(result.is_ok());
834
835 let selection = result.unwrap();
836 assert!(selection.contains(&RethRpcModule::Eth));
837 assert!(selection.contains(&RethRpcModule::Admin));
838 assert!(selection.contains(&RethRpcModule::Other("myCustomModule".to_string())));
839 assert!(selection.contains(&RethRpcModule::Other("anotherCustom".to_string())));
840 }
841
842 #[test]
843 fn test_rpc_module_all_excludes_custom() {
844 let all_selection = RpcModuleSelection::All;
846
847 assert!(all_selection.contains(&RethRpcModule::Eth));
849 assert!(all_selection.contains(&RethRpcModule::Admin));
850
851 assert_eq!(all_selection.len(), RethRpcModule::variant_count());
854 }
855
856 #[test]
857 fn test_rpc_module_equality_with_other() {
858 let other1 = RethRpcModule::Other("custom".to_string());
859 let other2 = RethRpcModule::Other("custom".to_string());
860 let other3 = RethRpcModule::Other("different".to_string());
861
862 assert_eq!(other1, other2);
863 assert_ne!(other1, other3);
864 assert_ne!(other1, RethRpcModule::Eth);
865 }
866
867 #[test]
868 fn test_rpc_module_is_other() {
869 assert!(!RethRpcModule::Eth.is_other());
871 assert!(!RethRpcModule::Admin.is_other());
872 assert!(!RethRpcModule::Debug.is_other());
873
874 assert!(RethRpcModule::Other("custom".to_string()).is_other());
876 assert!(RethRpcModule::Other("mycustomrpc".to_string()).is_other());
877 }
878
879 #[test]
880 fn test_standard_variant_names_excludes_other() {
881 let standard_names: Vec<_> = RethRpcModule::standard_variant_names().collect();
882
883 assert!(!standard_names.contains(&"other"));
885
886 assert_eq!(standard_names.len(), RethRpcModule::STANDARD_VARIANTS.len());
888
889 for variant in RethRpcModule::STANDARD_VARIANTS {
891 assert!(standard_names.contains(&variant.as_ref()));
892 }
893 }
894
895 #[test]
896 fn test_default_validator_accepts_standard_modules() {
897 let result = DefaultRpcModuleValidator::parse_selection("eth,admin,debug");
899 assert!(result.is_ok());
900
901 let selection = result.unwrap();
902 assert!(matches!(selection, RpcModuleSelection::Selection(_)));
903 }
904
905 #[test]
906 fn test_default_validator_rejects_unknown_modules() {
907 let result = DefaultRpcModuleValidator::parse_selection("eth,mycustom");
909 assert!(result.is_err());
910 assert!(result.unwrap_err().contains("Unknown RPC module: 'mycustom'"));
911
912 let result = DefaultRpcModuleValidator::parse_selection("unknownmodule");
913 assert!(result.is_err());
914 assert!(result.unwrap_err().contains("Unknown RPC module: 'unknownmodule'"));
915
916 let result = DefaultRpcModuleValidator::parse_selection("eth,admin,xyz123");
917 assert!(result.is_err());
918 assert!(result.unwrap_err().contains("Unknown RPC module: 'xyz123'"));
919 }
920
921 #[test]
922 fn test_default_validator_all_selection() {
923 let result = DefaultRpcModuleValidator::parse_selection("all");
925 assert!(result.is_ok());
926 assert_eq!(result.unwrap(), RpcModuleSelection::All);
927 }
928
929 #[test]
930 fn test_default_validator_none_selection() {
931 let result = DefaultRpcModuleValidator::parse_selection("none");
933 assert!(result.is_ok());
934 assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default()));
935 }
936
937 #[test]
938 fn test_lenient_validator_accepts_unknown_modules() {
939 let result = LenientRpcModuleValidator::parse_selection("eht,adimn,xyz123,customrpc");
941 assert!(result.is_ok());
942
943 let selection = result.unwrap();
944 if let RpcModuleSelection::Selection(modules) = selection {
945 assert!(modules.contains(&RethRpcModule::Other("eht".to_string())));
946 assert!(modules.contains(&RethRpcModule::Other("adimn".to_string())));
947 assert!(modules.contains(&RethRpcModule::Other("xyz123".to_string())));
948 assert!(modules.contains(&RethRpcModule::Other("customrpc".to_string())));
949 } else {
950 panic!("Expected Selection variant");
951 }
952 }
953
954 #[test]
955 fn test_default_validator_mixed_standard_and_custom() {
956 let result = DefaultRpcModuleValidator::parse_selection("eth,admin,mycustom,debug");
958 assert!(result.is_err());
959 assert!(result.unwrap_err().contains("Unknown RPC module: 'mycustom'"));
960 }
961}