fakecloud_core/
registry.rs1use crate::service::AwsService;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5#[derive(Default)]
7pub struct ServiceRegistry {
8 services: HashMap<String, Arc<dyn AwsService>>,
9}
10
11impl ServiceRegistry {
12 pub fn new() -> Self {
13 Self::default()
14 }
15
16 pub fn register(&mut self, service: Arc<dyn AwsService>) {
17 self.services
18 .insert(service.service_name().to_string(), service);
19 }
20
21 pub fn get(&self, name: &str) -> Option<&Arc<dyn AwsService>> {
22 self.services.get(name)
23 }
24
25 pub fn service_names(&self) -> Vec<&str> {
26 self.services.keys().map(|s| s.as_str()).collect()
27 }
28
29 pub fn iam_enforcement_split(&self) -> (Vec<&str>, Vec<&str>) {
33 let mut enforced: Vec<&str> = Vec::new();
34 let mut skipped: Vec<&str> = Vec::new();
35 for (name, service) in &self.services {
36 if service.iam_enforceable() {
37 enforced.push(name.as_str());
38 } else {
39 skipped.push(name.as_str());
40 }
41 }
42 enforced.sort();
43 skipped.sort();
44 (enforced, skipped)
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
52 use async_trait::async_trait;
53
54 struct EnforcedService {
55 name: &'static str,
56 }
57
58 #[async_trait]
59 impl AwsService for EnforcedService {
60 fn service_name(&self) -> &str {
61 self.name
62 }
63 async fn handle(&self, _: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
64 unreachable!()
65 }
66 fn supported_actions(&self) -> &[&str] {
67 &[]
68 }
69 fn iam_enforceable(&self) -> bool {
70 true
71 }
72 }
73
74 struct UnenforcedService {
75 name: &'static str,
76 }
77
78 #[async_trait]
79 impl AwsService for UnenforcedService {
80 fn service_name(&self) -> &str {
81 self.name
82 }
83 async fn handle(&self, _: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
84 unreachable!()
85 }
86 fn supported_actions(&self) -> &[&str] {
87 &[]
88 }
89 }
90
91 #[test]
92 fn new_is_empty() {
93 let r = ServiceRegistry::new();
94 assert!(r.service_names().is_empty());
95 assert!(r.get("anything").is_none());
96 }
97
98 #[test]
99 fn register_then_get_roundtrip() {
100 let mut r = ServiceRegistry::new();
101 r.register(Arc::new(EnforcedService { name: "s3" }));
102 assert!(r.get("s3").is_some());
103 assert!(r.get("missing").is_none());
104 }
105
106 #[test]
107 fn service_names_collects_registered() {
108 let mut r = ServiceRegistry::new();
109 r.register(Arc::new(EnforcedService { name: "s3" }));
110 r.register(Arc::new(UnenforcedService { name: "sts" }));
111 let mut names = r.service_names();
112 names.sort();
113 assert_eq!(names, vec!["s3", "sts"]);
114 }
115
116 #[test]
117 fn iam_split_sorts_and_separates_groups() {
118 let mut r = ServiceRegistry::new();
119 r.register(Arc::new(EnforcedService { name: "s3" }));
120 r.register(Arc::new(EnforcedService { name: "iam" }));
121 r.register(Arc::new(UnenforcedService { name: "sts" }));
122 r.register(Arc::new(UnenforcedService { name: "bedrock" }));
123 let (enforced, skipped) = r.iam_enforcement_split();
124 assert_eq!(enforced, vec!["iam", "s3"]);
125 assert_eq!(skipped, vec!["bedrock", "sts"]);
126 }
127
128 #[test]
129 fn register_overwrites_same_name() {
130 let mut r = ServiceRegistry::new();
131 r.register(Arc::new(EnforcedService { name: "s3" }));
132 r.register(Arc::new(UnenforcedService { name: "s3" }));
133 let (enforced, skipped) = r.iam_enforcement_split();
134 assert!(enforced.is_empty());
135 assert_eq!(skipped, vec!["s3"]);
136 }
137}