use std::{marker::PhantomData, sync::Arc}; use async_trait::async_trait; use crate::{protocol::Message, Client, ClientError}; pub(crate) type ServiceId = String; /// Returns a unique ID for the `Service` implementation specified as a type parameter. pub(crate) fn service_id() -> ServiceId { std::any::type_name::().to_owned() } /// A typed IPC service interface. /// /// Implementations should implement `ServiceImpl` and be registered on the server, while clients /// can use `ServiceCaller` to call the service. #[async_trait] pub trait Service: Send + Sync + 'static { type Request: Message + 'static; type Response: Message + 'static; } /// To be implemented for each IPC service, where a service has a defined request/response type. /// /// This should be implemented and is registered on the "Server" side. /// /// Though it is technically up to users to determine whether to use a collection of `Service`s /// or implement a single service that delegates internally, prefer the former. #[async_trait] pub trait ServiceImpl: 'static + Send + Sync + Clone { type Service: Service; async fn handle_request( &self, request: <::Service as Service>::Request, ) -> <::Service as Service>::Response; } /// Provides an typed interface to call an underlying `Service`. /// /// Usage: /// /// ```ignore /// let client = Arc::new( /// Client::connect(connection_address, executor) /// .await /// .expect("Failed to connect client."), /// ); /// let foo = ServiceCaller::::new(client); /// let response = foo.call(FooRequest {}).await; /// ``` #[async_trait] pub trait ServiceCaller: Send + Sync { async fn call(&self, request: S::Request) -> Result; } /// Returns a `ServiceCaller` implementation for the service `S`. pub fn service_caller(client: Arc) -> Box> { Box::new(RealServiceCaller::new(client)) } /// Real `ServiceCaller` implementation. struct RealServiceCaller { client: Arc, _service_type_marker: PhantomData, } impl RealServiceCaller where S: Service, { fn new(client: Arc) -> Self { Self { client, _service_type_marker: PhantomData, } } } #[async_trait] impl ServiceCaller for RealServiceCaller where S: Service, { /// Sends the given request and returns a `Result` containing its response. async fn call(&self, request: S::Request) -> Result { let request_bytes = bincode::serialize(&request).expect("Failed to serialize request."); self.client .send_request::(request_bytes) .await .map(|response_bytes| { bincode::deserialize::(&response_bytes[..]) .expect("Failed to deserialize response.") }) } }