use anyhow::Result;
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
use std::pin::Pin;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RpcMode {
Unary,
ServerStream,
ClientStream,
Bidi,
}
#[derive(Debug, Clone)]
pub struct EndpointMeta {
pub rpc_mode: RpcMode,
pub input_type: Option<String>,
pub output_type: Option<String>,
}
#[derive(Debug, Clone)]
pub enum CallStreamItem {
Message(Value),
Trailers(HashMap<String, String>),
}
#[derive(Debug, Clone)]
pub struct CallError {
pub code: i32,
pub message: String,
}
pub enum CallRequest {
Unary(Value),
Streaming(Pin<Box<dyn futures::Stream<Item = Value> + Send>>),
}
#[async_trait]
pub trait CallClient: Send {
async fn resolve_endpoint(&self, endpoint: &str) -> Result<EndpointMeta>;
async fn call(
&mut self,
endpoint: &str,
request: CallRequest,
) -> Result<Pin<Box<dyn futures::Stream<Item = Result<CallStreamItem, CallError>> + Send>>>;
}
#[async_trait]
pub trait CallClientFactory: Send + Sync {
async fn create_client(
&self,
config: &crate::config::CallClientConfig,
) -> Result<Box<dyn CallClient>>;
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_rpc_mode_debug() {
assert_eq!(format!("{:?}", RpcMode::Unary), "Unary");
assert_eq!(format!("{:?}", RpcMode::ServerStream), "ServerStream");
assert_eq!(format!("{:?}", RpcMode::ClientStream), "ClientStream");
assert_eq!(format!("{:?}", RpcMode::Bidi), "Bidi");
}
#[test]
fn test_call_error_new() {
let err = CallError {
code: 5,
message: "not found".into(),
};
assert_eq!(err.code, 5);
assert_eq!(err.message, "not found");
}
#[test]
fn test_call_stream_item_message() {
let item = CallStreamItem::Message(serde_json::json!({"key": "value"}));
match item {
CallStreamItem::Message(v) => assert_eq!(v["key"], "value"),
_ => panic!("expected Message"),
}
}
#[test]
fn test_call_stream_item_trailers() {
let mut h = HashMap::new();
h.insert("x-status".into(), "ok".into());
let item = CallStreamItem::Trailers(h);
match item {
CallStreamItem::Trailers(t) => assert_eq!(t.get("x-status"), Some(&"ok".into())),
_ => panic!("expected Trailers"),
}
}
#[test]
fn test_endpoint_meta_new() {
let meta = EndpointMeta {
rpc_mode: RpcMode::Unary,
input_type: Some("test.Request".into()),
output_type: Some("test.Response".into()),
};
assert_eq!(meta.rpc_mode, RpcMode::Unary);
assert_eq!(meta.input_type.as_deref(), Some("test.Request"));
}
}