use std::future::Future;
use std::pin::Pin;
use super::status::Status;
use super::streaming::{Request, Response, Streaming};
pub trait Method: Send + Sync + 'static {
type Request: Send + 'static;
type Response: Send + 'static;
type Future: Future<Output = Result<Response<Self::Response>, Status>> + Send;
fn call(&self, request: Request<Self::Request>) -> Self::Future;
}
pub trait NamedService {
const NAME: &'static str;
}
pub trait UnaryMethod<Req, Resp>: Send + Sync + 'static
where
Req: Send + 'static,
Resp: Send + 'static,
{
fn call(
&self,
request: Request<Req>,
) -> Pin<Box<dyn Future<Output = Result<Response<Resp>, Status>> + Send>>;
}
#[allow(clippy::type_complexity)]
pub trait ServerStreamingMethod<Req, Resp>: Send + Sync + 'static
where
Req: Send + 'static,
Resp: Send + 'static,
{
type Stream: Streaming<Message = Resp> + Send + 'static;
fn call(
&self,
request: Request<Req>,
) -> Pin<Box<dyn Future<Output = Result<Response<Self::Stream>, Status>> + Send>>;
}
pub trait ClientStreamingMethod<Req, Resp>: Send + Sync + 'static
where
Req: Send + 'static,
Resp: Send + 'static,
{
type Stream: Streaming<Message = Req> + Send + 'static;
fn call(
&self,
request: Request<Self::Stream>,
) -> Pin<Box<dyn Future<Output = Result<Response<Resp>, Status>> + Send>>;
}
#[allow(clippy::type_complexity)]
pub trait BidiStreamingMethod<Req, Resp>: Send + Sync + 'static
where
Req: Send + 'static,
Resp: Send + 'static,
{
type RequestStream: Streaming<Message = Req> + Send + 'static;
type ResponseStream: Streaming<Message = Resp> + Send + 'static;
fn call(
&self,
request: Request<Self::RequestStream>,
) -> Pin<Box<dyn Future<Output = Result<Response<Self::ResponseStream>, Status>> + Send>>;
}
#[derive(Debug, Clone)]
pub struct MethodDescriptor {
pub name: &'static str,
pub path: &'static str,
pub client_streaming: bool,
pub server_streaming: bool,
}
impl MethodDescriptor {
#[must_use]
pub const fn unary(name: &'static str, path: &'static str) -> Self {
Self {
name,
path,
client_streaming: false,
server_streaming: false,
}
}
#[must_use]
pub const fn server_streaming(name: &'static str, path: &'static str) -> Self {
Self {
name,
path,
client_streaming: false,
server_streaming: true,
}
}
#[must_use]
pub const fn client_streaming(name: &'static str, path: &'static str) -> Self {
Self {
name,
path,
client_streaming: true,
server_streaming: false,
}
}
#[must_use]
pub const fn bidi_streaming(name: &'static str, path: &'static str) -> Self {
Self {
name,
path,
client_streaming: true,
server_streaming: true,
}
}
#[must_use]
pub const fn is_unary(&self) -> bool {
!self.client_streaming && !self.server_streaming
}
}
#[derive(Debug, Clone)]
pub struct ServiceDescriptor {
pub name: &'static str,
pub package: &'static str,
pub methods: &'static [MethodDescriptor],
}
impl ServiceDescriptor {
#[must_use]
pub const fn new(
name: &'static str,
package: &'static str,
methods: &'static [MethodDescriptor],
) -> Self {
Self {
name,
package,
methods,
}
}
#[must_use]
pub fn full_name(&self) -> String {
if self.package.is_empty() {
self.name.to_string()
} else {
format!("{}.{}", self.package, self.name)
}
}
}
pub type UnaryHandler<Req, Resp> = Box<
dyn Fn(Request<Req>) -> Pin<Box<dyn Future<Output = Result<Response<Resp>, Status>> + Send>>
+ Send
+ Sync,
>;
pub trait ServiceHandler: Send + Sync {
fn descriptor(&self) -> &ServiceDescriptor;
fn method_names(&self) -> Vec<&str>;
}
#[cfg(test)]
mod tests {
use super::*;
static METHODS_GREETER: &[MethodDescriptor] = &[MethodDescriptor::unary(
"SayHello",
"/helloworld.Greeter/SayHello",
)];
static METHODS_EMPTY: &[MethodDescriptor] = &[];
fn init_test(name: &str) {
crate::test_utils::init_test_logging();
crate::test_phase!(name);
}
#[test]
fn test_method_descriptor_unary() {
init_test("test_method_descriptor_unary");
let desc = MethodDescriptor::unary("SayHello", "/helloworld.Greeter/SayHello");
let unary = desc.is_unary();
crate::assert_with_log!(unary, "is_unary", true, unary);
crate::assert_with_log!(
!desc.client_streaming,
"client_streaming",
false,
desc.client_streaming
);
crate::assert_with_log!(
!desc.server_streaming,
"server_streaming",
false,
desc.server_streaming
);
crate::test_complete!("test_method_descriptor_unary");
}
#[test]
fn test_method_descriptor_server_streaming() {
init_test("test_method_descriptor_server_streaming");
let desc =
MethodDescriptor::server_streaming("ListFeatures", "/route.RouteGuide/ListFeatures");
let unary = desc.is_unary();
crate::assert_with_log!(!unary, "not unary", false, unary);
crate::assert_with_log!(
!desc.client_streaming,
"client_streaming",
false,
desc.client_streaming
);
crate::assert_with_log!(
desc.server_streaming,
"server_streaming",
true,
desc.server_streaming
);
crate::test_complete!("test_method_descriptor_server_streaming");
}
#[test]
fn test_method_descriptor_bidi() {
init_test("test_method_descriptor_bidi");
let desc = MethodDescriptor::bidi_streaming("RouteChat", "/route.RouteGuide/RouteChat");
crate::assert_with_log!(
desc.client_streaming,
"client_streaming",
true,
desc.client_streaming
);
crate::assert_with_log!(
desc.server_streaming,
"server_streaming",
true,
desc.server_streaming
);
crate::test_complete!("test_method_descriptor_bidi");
}
#[test]
fn test_service_descriptor() {
init_test("test_service_descriptor");
let desc = ServiceDescriptor::new("Greeter", "helloworld", METHODS_GREETER);
let name = desc.full_name();
crate::assert_with_log!(
name == "helloworld.Greeter",
"full_name",
"helloworld.Greeter",
name
);
let len = desc.methods.len();
crate::assert_with_log!(len == 1, "methods len", 1, len);
crate::test_complete!("test_service_descriptor");
}
#[test]
fn test_service_descriptor_no_package() {
init_test("test_service_descriptor_no_package");
let desc = ServiceDescriptor::new("Service", "", METHODS_EMPTY);
let name = desc.full_name();
crate::assert_with_log!(name == "Service", "full_name", "Service", name);
crate::test_complete!("test_service_descriptor_no_package");
}
#[test]
fn method_descriptor_debug_clone() {
let md = MethodDescriptor::unary("Hello", "/pkg.Svc/Hello");
let dbg = format!("{md:?}");
assert!(dbg.contains("MethodDescriptor"), "{dbg}");
assert!(dbg.contains("Hello"), "{dbg}");
let cloned = md.clone();
assert_eq!(cloned.name, md.name);
assert_eq!(cloned.path, md.path);
assert_eq!(cloned.client_streaming, md.client_streaming);
assert_eq!(cloned.server_streaming, md.server_streaming);
}
#[test]
fn method_descriptor_client_streaming() {
let md = MethodDescriptor::client_streaming("Upload", "/pkg.Svc/Upload");
assert!(md.client_streaming);
assert!(!md.server_streaming);
assert!(!md.is_unary());
}
#[test]
fn service_descriptor_debug_clone() {
let desc = ServiceDescriptor::new("Greeter", "helloworld", METHODS_GREETER);
let dbg = format!("{desc:?}");
assert!(dbg.contains("ServiceDescriptor"), "{dbg}");
let cloned = desc;
assert_eq!(cloned.name, "Greeter");
assert_eq!(cloned.package, "helloworld");
assert_eq!(cloned.methods.len(), 1);
}
}