use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use lazy_static::lazy_static;
use crate::proto::{CompareContentsRequest, CompareContentsResponse, GenerateContentRequest, GenerateContentResponse};
use crate::proto_v2::{GenerateFieldRequest, GenerateFieldResponse, MatchFieldRequest, MatchFieldResponse};
#[async_trait]
pub trait CoreContentMatcher: Send + Sync {
async fn compare_contents(&self, request: CompareContentsRequest) -> anyhow::Result<CompareContentsResponse>;
}
#[async_trait]
pub trait CoreFieldMatcher: Send + Sync {
async fn match_field(&self, request: MatchFieldRequest) -> anyhow::Result<MatchFieldResponse>;
}
#[async_trait]
pub trait CoreFieldGenerator: Send + Sync {
async fn generate_field(&self, request: GenerateFieldRequest) -> anyhow::Result<GenerateFieldResponse>;
}
#[async_trait]
pub trait CoreContentGenerator: Send + Sync {
async fn generate_content(&self, request: GenerateContentRequest) -> anyhow::Result<GenerateContentResponse>;
}
lazy_static! {
static ref CORE_CONTENT_MATCHERS: Mutex<HashMap<String, Arc<dyn CoreContentMatcher>>> = Mutex::new(HashMap::new());
static ref CORE_CONTENT_GENERATORS: Mutex<HashMap<String, Arc<dyn CoreContentGenerator>>> = Mutex::new(HashMap::new());
static ref CORE_FIELD_MATCHERS: Mutex<HashMap<String, Arc<dyn CoreFieldMatcher>>> = Mutex::new(HashMap::new());
static ref CORE_FIELD_GENERATORS: Mutex<HashMap<String, Arc<dyn CoreFieldGenerator>>> = Mutex::new(HashMap::new());
}
pub fn register_core_content_matcher(key: &str, handler: Arc<dyn CoreContentMatcher>) {
CORE_CONTENT_MATCHERS.lock()
.expect("CORE_CONTENT_MATCHERS mutex poisoned")
.insert(key.to_string(), handler);
}
pub fn register_core_content_generator(key: &str, handler: Arc<dyn CoreContentGenerator>) {
CORE_CONTENT_GENERATORS.lock()
.expect("CORE_CONTENT_GENERATORS mutex poisoned")
.insert(key.to_string(), handler);
}
pub fn lookup_core_content_matcher(key: &str) -> Option<Arc<dyn CoreContentMatcher>> {
CORE_CONTENT_MATCHERS.lock()
.expect("CORE_CONTENT_MATCHERS mutex poisoned")
.get(key).cloned()
}
pub fn lookup_core_content_generator(key: &str) -> Option<Arc<dyn CoreContentGenerator>> {
CORE_CONTENT_GENERATORS.lock()
.expect("CORE_CONTENT_GENERATORS mutex poisoned")
.get(key).cloned()
}
pub fn register_core_field_matcher(key: &str, handler: Arc<dyn CoreFieldMatcher>) {
CORE_FIELD_MATCHERS.lock()
.expect("CORE_FIELD_MATCHERS mutex poisoned")
.insert(key.to_string(), handler);
}
pub fn register_core_field_generator(key: &str, handler: Arc<dyn CoreFieldGenerator>) {
CORE_FIELD_GENERATORS.lock()
.expect("CORE_FIELD_GENERATORS mutex poisoned")
.insert(key.to_string(), handler);
}
pub fn lookup_core_field_matcher(key: &str) -> Option<Arc<dyn CoreFieldMatcher>> {
CORE_FIELD_MATCHERS.lock()
.expect("CORE_FIELD_MATCHERS mutex poisoned")
.get(key).cloned()
}
pub fn lookup_core_field_generator(key: &str) -> Option<Arc<dyn CoreFieldGenerator>> {
CORE_FIELD_GENERATORS.lock()
.expect("CORE_FIELD_GENERATORS mutex poisoned")
.get(key).cloned()
}
pub fn deregister_core_field_matcher(key: &str) {
CORE_FIELD_MATCHERS.lock()
.expect("CORE_FIELD_MATCHERS mutex poisoned")
.remove(key);
}
pub fn deregister_core_field_generator(key: &str) {
CORE_FIELD_GENERATORS.lock()
.expect("CORE_FIELD_GENERATORS mutex poisoned")
.remove(key);
}
pub fn deregister_core_content_matcher(key: &str) {
CORE_CONTENT_MATCHERS.lock()
.expect("CORE_CONTENT_MATCHERS mutex poisoned")
.remove(key);
}
pub fn deregister_core_content_generator(key: &str) {
CORE_CONTENT_GENERATORS.lock()
.expect("CORE_CONTENT_GENERATORS mutex poisoned")
.remove(key);
}
#[cfg(test)]
mod tests {
use expectest::prelude::*;
use crate::proto::{CompareContentsRequest, CompareContentsResponse, GenerateContentRequest, GenerateContentResponse};
use crate::proto_v2::{GenerateFieldRequest, GenerateFieldResponse, MatchFieldRequest, MatchFieldResponse};
use super::*;
#[derive(Debug)]
struct TestMatcher;
#[async_trait]
impl CoreContentMatcher for TestMatcher {
async fn compare_contents(&self, _request: CompareContentsRequest) -> anyhow::Result<CompareContentsResponse> {
Ok(CompareContentsResponse::default())
}
}
#[derive(Debug)]
struct TestGenerator;
#[async_trait]
impl CoreContentGenerator for TestGenerator {
async fn generate_content(&self, _request: GenerateContentRequest) -> anyhow::Result<GenerateContentResponse> {
Ok(GenerateContentResponse::default())
}
}
#[test_log::test]
fn returns_none_for_an_unregistered_key() {
expect!(lookup_core_content_matcher("unregistered-matcher-key").is_none()).to(be_true());
expect!(lookup_core_content_generator("unregistered-generator-key").is_none()).to(be_true());
}
#[test_log::test(tokio::test)]
async fn registers_and_looks_up_a_content_matcher() {
register_core_content_matcher("test-matcher-key", Arc::new(TestMatcher));
let handler = lookup_core_content_matcher("test-matcher-key");
deregister_core_content_matcher("test-matcher-key");
expect!(handler.is_some()).to(be_true());
let response = handler.unwrap().compare_contents(CompareContentsRequest::default()).await;
expect!(response.is_ok()).to(be_true());
}
#[test_log::test(tokio::test)]
async fn registers_and_looks_up_a_content_generator() {
register_core_content_generator("test-generator-key", Arc::new(TestGenerator));
let handler = lookup_core_content_generator("test-generator-key");
deregister_core_content_generator("test-generator-key");
expect!(handler.is_some()).to(be_true());
let response = handler.unwrap().generate_content(GenerateContentRequest::default()).await;
expect!(response.is_ok()).to(be_true());
}
#[test_log::test]
fn deregister_is_a_no_op_for_an_unknown_key() {
deregister_core_content_matcher("never-registered");
deregister_core_content_generator("never-registered");
deregister_core_field_matcher("never-registered");
deregister_core_field_generator("never-registered");
}
#[derive(Debug)]
struct TestFieldMatcher;
#[async_trait]
impl CoreFieldMatcher for TestFieldMatcher {
async fn match_field(&self, request: MatchFieldRequest) -> anyhow::Result<MatchFieldResponse> {
Ok(MatchFieldResponse { error: request.key, .. MatchFieldResponse::default() })
}
}
#[derive(Debug)]
struct TestFieldGenerator;
#[async_trait]
impl CoreFieldGenerator for TestFieldGenerator {
async fn generate_field(&self, request: GenerateFieldRequest) -> anyhow::Result<GenerateFieldResponse> {
Ok(GenerateFieldResponse { error: request.key, .. GenerateFieldResponse::default() })
}
}
#[test_log::test]
fn returns_none_for_an_unregistered_field_key() {
expect!(lookup_core_field_matcher("unregistered-field-matcher-key").is_none()).to(be_true());
expect!(lookup_core_field_generator("unregistered-field-generator-key").is_none()).to(be_true());
}
#[test_log::test(tokio::test)]
async fn registers_and_looks_up_a_field_matcher() {
register_core_field_matcher("test-field-matcher-key", Arc::new(TestFieldMatcher));
let handler = lookup_core_field_matcher("test-field-matcher-key");
deregister_core_field_matcher("test-field-matcher-key");
expect!(handler.is_some()).to(be_true());
let response = handler.unwrap()
.match_field(MatchFieldRequest { key: "type".to_string(), .. MatchFieldRequest::default() })
.await;
expect!(response.unwrap().error).to(be_equal_to("type".to_string()));
}
#[test_log::test(tokio::test)]
async fn registers_and_looks_up_a_field_generator() {
register_core_field_generator("test-field-generator-key", Arc::new(TestFieldGenerator));
let handler = lookup_core_field_generator("test-field-generator-key");
deregister_core_field_generator("test-field-generator-key");
expect!(handler.is_some()).to(be_true());
let response = handler.unwrap()
.generate_field(GenerateFieldRequest { key: "date".to_string(), .. GenerateFieldRequest::default() })
.await;
expect!(response.unwrap().error).to(be_equal_to("date".to_string()));
}
}