pact_plugin_driver/
core_capabilities.rs1use std::collections::HashMap;
15use std::sync::{Arc, Mutex};
16
17use async_trait::async_trait;
18use lazy_static::lazy_static;
19
20use crate::proto::{CompareContentsRequest, CompareContentsResponse, GenerateContentRequest, GenerateContentResponse};
21use crate::proto_v2::{GenerateFieldRequest, GenerateFieldResponse, MatchFieldRequest, MatchFieldResponse};
22
23#[async_trait]
26pub trait CoreContentMatcher: Send + Sync {
27 async fn compare_contents(&self, request: CompareContentsRequest) -> anyhow::Result<CompareContentsResponse>;
29}
30
31#[async_trait]
39pub trait CoreFieldMatcher: Send + Sync {
40 async fn match_field(&self, request: MatchFieldRequest) -> anyhow::Result<MatchFieldResponse>;
42}
43
44#[async_trait]
48pub trait CoreFieldGenerator: Send + Sync {
49 async fn generate_field(&self, request: GenerateFieldRequest) -> anyhow::Result<GenerateFieldResponse>;
51}
52
53#[async_trait]
56pub trait CoreContentGenerator: Send + Sync {
57 async fn generate_content(&self, request: GenerateContentRequest) -> anyhow::Result<GenerateContentResponse>;
59}
60
61lazy_static! {
62 static ref CORE_CONTENT_MATCHERS: Mutex<HashMap<String, Arc<dyn CoreContentMatcher>>> = Mutex::new(HashMap::new());
63 static ref CORE_CONTENT_GENERATORS: Mutex<HashMap<String, Arc<dyn CoreContentGenerator>>> = Mutex::new(HashMap::new());
64 static ref CORE_FIELD_MATCHERS: Mutex<HashMap<String, Arc<dyn CoreFieldMatcher>>> = Mutex::new(HashMap::new());
65 static ref CORE_FIELD_GENERATORS: Mutex<HashMap<String, Arc<dyn CoreFieldGenerator>>> = Mutex::new(HashMap::new());
66}
67
68pub fn register_core_content_matcher(key: &str, handler: Arc<dyn CoreContentMatcher>) {
72 CORE_CONTENT_MATCHERS.lock()
73 .expect("CORE_CONTENT_MATCHERS mutex poisoned")
74 .insert(key.to_string(), handler);
75}
76
77pub fn register_core_content_generator(key: &str, handler: Arc<dyn CoreContentGenerator>) {
81 CORE_CONTENT_GENERATORS.lock()
82 .expect("CORE_CONTENT_GENERATORS mutex poisoned")
83 .insert(key.to_string(), handler);
84}
85
86pub fn lookup_core_content_matcher(key: &str) -> Option<Arc<dyn CoreContentMatcher>> {
88 CORE_CONTENT_MATCHERS.lock()
89 .expect("CORE_CONTENT_MATCHERS mutex poisoned")
90 .get(key).cloned()
91}
92
93pub fn lookup_core_content_generator(key: &str) -> Option<Arc<dyn CoreContentGenerator>> {
95 CORE_CONTENT_GENERATORS.lock()
96 .expect("CORE_CONTENT_GENERATORS mutex poisoned")
97 .get(key).cloned()
98}
99
100pub fn register_core_field_matcher(key: &str, handler: Arc<dyn CoreFieldMatcher>) {
104 CORE_FIELD_MATCHERS.lock()
105 .expect("CORE_FIELD_MATCHERS mutex poisoned")
106 .insert(key.to_string(), handler);
107}
108
109pub fn register_core_field_generator(key: &str, handler: Arc<dyn CoreFieldGenerator>) {
113 CORE_FIELD_GENERATORS.lock()
114 .expect("CORE_FIELD_GENERATORS mutex poisoned")
115 .insert(key.to_string(), handler);
116}
117
118pub fn lookup_core_field_matcher(key: &str) -> Option<Arc<dyn CoreFieldMatcher>> {
120 CORE_FIELD_MATCHERS.lock()
121 .expect("CORE_FIELD_MATCHERS mutex poisoned")
122 .get(key).cloned()
123}
124
125pub fn lookup_core_field_generator(key: &str) -> Option<Arc<dyn CoreFieldGenerator>> {
127 CORE_FIELD_GENERATORS.lock()
128 .expect("CORE_FIELD_GENERATORS mutex poisoned")
129 .get(key).cloned()
130}
131
132pub fn deregister_core_field_matcher(key: &str) {
134 CORE_FIELD_MATCHERS.lock()
135 .expect("CORE_FIELD_MATCHERS mutex poisoned")
136 .remove(key);
137}
138
139pub fn deregister_core_field_generator(key: &str) {
141 CORE_FIELD_GENERATORS.lock()
142 .expect("CORE_FIELD_GENERATORS mutex poisoned")
143 .remove(key);
144}
145
146pub fn deregister_core_content_matcher(key: &str) {
148 CORE_CONTENT_MATCHERS.lock()
149 .expect("CORE_CONTENT_MATCHERS mutex poisoned")
150 .remove(key);
151}
152
153pub fn deregister_core_content_generator(key: &str) {
155 CORE_CONTENT_GENERATORS.lock()
156 .expect("CORE_CONTENT_GENERATORS mutex poisoned")
157 .remove(key);
158}
159
160#[cfg(test)]
161mod tests {
162 use expectest::prelude::*;
163
164 use crate::proto::{CompareContentsRequest, CompareContentsResponse, GenerateContentRequest, GenerateContentResponse};
165 use crate::proto_v2::{GenerateFieldRequest, GenerateFieldResponse, MatchFieldRequest, MatchFieldResponse};
166
167 use super::*;
168
169 #[derive(Debug)]
170 struct TestMatcher;
171
172 #[async_trait]
173 impl CoreContentMatcher for TestMatcher {
174 async fn compare_contents(&self, _request: CompareContentsRequest) -> anyhow::Result<CompareContentsResponse> {
175 Ok(CompareContentsResponse::default())
176 }
177 }
178
179 #[derive(Debug)]
180 struct TestGenerator;
181
182 #[async_trait]
183 impl CoreContentGenerator for TestGenerator {
184 async fn generate_content(&self, _request: GenerateContentRequest) -> anyhow::Result<GenerateContentResponse> {
185 Ok(GenerateContentResponse::default())
186 }
187 }
188
189 #[test_log::test]
190 fn returns_none_for_an_unregistered_key() {
191 expect!(lookup_core_content_matcher("unregistered-matcher-key").is_none()).to(be_true());
192 expect!(lookup_core_content_generator("unregistered-generator-key").is_none()).to(be_true());
193 }
194
195 #[test_log::test(tokio::test)]
196 async fn registers_and_looks_up_a_content_matcher() {
197 register_core_content_matcher("test-matcher-key", Arc::new(TestMatcher));
198
199 let handler = lookup_core_content_matcher("test-matcher-key");
200 deregister_core_content_matcher("test-matcher-key");
201
202 expect!(handler.is_some()).to(be_true());
203 let response = handler.unwrap().compare_contents(CompareContentsRequest::default()).await;
204 expect!(response.is_ok()).to(be_true());
205 }
206
207 #[test_log::test(tokio::test)]
208 async fn registers_and_looks_up_a_content_generator() {
209 register_core_content_generator("test-generator-key", Arc::new(TestGenerator));
210
211 let handler = lookup_core_content_generator("test-generator-key");
212 deregister_core_content_generator("test-generator-key");
213
214 expect!(handler.is_some()).to(be_true());
215 let response = handler.unwrap().generate_content(GenerateContentRequest::default()).await;
216 expect!(response.is_ok()).to(be_true());
217 }
218
219 #[test_log::test]
220 fn deregister_is_a_no_op_for_an_unknown_key() {
221 deregister_core_content_matcher("never-registered");
222 deregister_core_content_generator("never-registered");
223 deregister_core_field_matcher("never-registered");
224 deregister_core_field_generator("never-registered");
225 }
226
227 #[derive(Debug)]
228 struct TestFieldMatcher;
229
230 #[async_trait]
231 impl CoreFieldMatcher for TestFieldMatcher {
232 async fn match_field(&self, request: MatchFieldRequest) -> anyhow::Result<MatchFieldResponse> {
233 Ok(MatchFieldResponse { error: request.key, .. MatchFieldResponse::default() })
234 }
235 }
236
237 #[derive(Debug)]
238 struct TestFieldGenerator;
239
240 #[async_trait]
241 impl CoreFieldGenerator for TestFieldGenerator {
242 async fn generate_field(&self, request: GenerateFieldRequest) -> anyhow::Result<GenerateFieldResponse> {
243 Ok(GenerateFieldResponse { error: request.key, .. GenerateFieldResponse::default() })
244 }
245 }
246
247 #[test_log::test]
248 fn returns_none_for_an_unregistered_field_key() {
249 expect!(lookup_core_field_matcher("unregistered-field-matcher-key").is_none()).to(be_true());
250 expect!(lookup_core_field_generator("unregistered-field-generator-key").is_none()).to(be_true());
251 }
252
253 #[test_log::test(tokio::test)]
254 async fn registers_and_looks_up_a_field_matcher() {
255 register_core_field_matcher("test-field-matcher-key", Arc::new(TestFieldMatcher));
256
257 let handler = lookup_core_field_matcher("test-field-matcher-key");
258 deregister_core_field_matcher("test-field-matcher-key");
259
260 expect!(handler.is_some()).to(be_true());
261 let response = handler.unwrap()
262 .match_field(MatchFieldRequest { key: "type".to_string(), .. MatchFieldRequest::default() })
263 .await;
264 expect!(response.unwrap().error).to(be_equal_to("type".to_string()));
266 }
267
268 #[test_log::test(tokio::test)]
269 async fn registers_and_looks_up_a_field_generator() {
270 register_core_field_generator("test-field-generator-key", Arc::new(TestFieldGenerator));
271
272 let handler = lookup_core_field_generator("test-field-generator-key");
273 deregister_core_field_generator("test-field-generator-key");
274
275 expect!(handler.is_some()).to(be_true());
276 let response = handler.unwrap()
277 .generate_field(GenerateFieldRequest { key: "date".to_string(), .. GenerateFieldRequest::default() })
278 .await;
279 expect!(response.unwrap().error).to(be_equal_to("date".to_string()));
280 }
281}