use super::super::error::GResult;
use super::super::registry::{Adapter, AdapterCall};
use async_trait::async_trait;
use serde_json::Value;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
type AdapterFuture = dyn Future<Output = GResult<Value>> + Send;
type AdapterInvoker = dyn Fn(AdapterCall) -> Pin<Box<AdapterFuture>> + Send + Sync;
#[async_trait]
pub trait AdapterBridge: Send + Sync {
async fn invoke(&self, call: AdapterCall) -> GResult<Value>;
}
#[async_trait]
impl<T> Adapter for T
where
T: AdapterBridge,
{
async fn call(&self, call: &AdapterCall) -> GResult<Value> {
self.invoke(call.clone()).await
}
}
pub struct FnAdapterBridge {
inner: Arc<AdapterInvoker>,
}
impl FnAdapterBridge {
pub fn new<F, Fut>(func: F) -> Self
where
F: Send + Sync + 'static + Fn(AdapterCall) -> Fut,
Fut: Future<Output = GResult<Value>> + Send + 'static,
{
let invoker: Arc<AdapterInvoker> = Arc::new(move |call: AdapterCall| {
let fut = func(call);
Box::pin(fut) as Pin<Box<AdapterFuture>>
});
Self { inner: invoker }
}
}
#[async_trait]
impl AdapterBridge for FnAdapterBridge {
async fn invoke(&self, call: AdapterCall) -> GResult<Value> {
(self.inner)(call).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[tokio::test]
async fn fn_adapter_bridge_invokes_closure() {
let bridge = FnAdapterBridge::new(|call: AdapterCall| async move {
Ok(json!({
"adapter": call.adapter,
"operation": call.operation,
"payload": call.payload,
}))
});
let call = AdapterCall {
adapter: "legacy".into(),
operation: "run".into(),
payload: json!({"ok": true}),
};
let result = bridge.invoke(call).await.expect("invoke");
assert_eq!(result["adapter"], "legacy");
assert_eq!(result["operation"], "run");
assert_eq!(result["payload"]["ok"], true);
}
#[tokio::test]
async fn adapter_trait_forwards_to_bridge() {
let bridge =
FnAdapterBridge::new(
|call: AdapterCall| async move { Ok(json!({ "echo": call.payload })) },
);
let call = AdapterCall {
adapter: "legacy".into(),
operation: "echo".into(),
payload: json!({"value": 7}),
};
let result = Adapter::call(&bridge, &call).await.expect("adapter call");
assert_eq!(result["echo"]["value"], 7);
}
}