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};
#[async_trait]
pub trait CoreContentMatcher: Send + Sync {
async fn compare_contents(&self, request: CompareContentsRequest) -> anyhow::Result<CompareContentsResponse>;
}
#[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());
}
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 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 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");
}
}