diff --git a/server/src/world.rs b/server/src/world.rs index 6adb8e2..d995d69 100644 --- a/server/src/world.rs +++ b/server/src/world.rs @@ -1,10 +1,18 @@ //! `world.proto` +pub mod error; +pub mod grpc; +mod r#impl; + +use std::sync::Arc; + use futures::future::BoxFuture; use serde::{Deserialize, Serialize}; use crate::prelude::IntoStatus; +pub use error::Error; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Size { pub width: u32, @@ -78,5 +86,17 @@ pub trait ProvideWorldService: Send + Sync + 'static { self.world_service().check_coordinate(ctx, req) } - // TODO: build_server(this: Arc) -> WorldServiceServer<...> + fn build_server(this: Arc) -> WorldServiceServer + where + Self: Sized, + { + let service = grpc::ServiceImpl::new(this); + WorldServiceServer::new(service) + } } + +#[derive(Debug, Clone, Copy)] +pub struct WorldServiceImpl; + +pub type WorldServiceServer = + schema::world::world_service_server::WorldServiceServer>; diff --git a/server/src/world/error.rs b/server/src/world/error.rs new file mode 100644 index 0000000..7d47827 --- /dev/null +++ b/server/src/world/error.rs @@ -0,0 +1,11 @@ +/// infallible +#[derive(Debug, Clone, thiserror::Error)] +pub enum Error {} + +impl From for tonic::Status { + fn from(value: Error) -> Self { + match value {} + } +} + +pub type Result = std::result::Result; diff --git a/server/src/world/grpc.rs b/server/src/world/grpc.rs new file mode 100644 index 0000000..203072b --- /dev/null +++ b/server/src/world/grpc.rs @@ -0,0 +1,84 @@ +use std::sync::Arc; + +use schema::world as schema; + +use crate::prelude::IntoStatus; + +// MARK: type conversions + +impl From for schema::Coordinate { + fn from(value: super::Coordinate) -> Self { + let super::Coordinate { x, y } = value; + Self { x, y } + } +} + +impl From for super::Coordinate { + fn from(value: schema::Coordinate) -> Self { + let schema::Coordinate { x, y } = value; + Self { x, y } + } +} + +impl From for schema::Size { + fn from(value: super::Size) -> Self { + let super::Size { width, height } = value; + Self { width, height } + } +} + +impl From for super::Size { + fn from(value: schema::Size) -> Self { + let schema::Size { width, height } = value; + Self { width, height } + } +} + +// MARK: ServiceImpl + +pub struct ServiceImpl { + state: Arc, +} + +impl Clone for ServiceImpl +where + State: super::ProvideWorldService, +{ + fn clone(&self) -> Self { + Self { + state: Arc::clone(&self.state), + } + } +} + +impl ServiceImpl +where + State: super::ProvideWorldService, +{ + pub(super) fn new(state: Arc) -> Self { + Self { state } + } +} + +#[async_trait::async_trait] +impl schema::world_service_server::WorldService for ServiceImpl +where + State: super::ProvideWorldService, +{ + async fn get_world( + &self, + request: tonic::Request, + ) -> tonic::Result> { + let (_, _, schema::GetWorldRequest {}) = request.into_parts(); + let req = super::GetWorldSize {}; + let size = self + .state + .get_world_size(req) + .await + .map_err(IntoStatus::into_status)? + .into(); + let world = schema::World { size: Some(size) }; + let res = schema::GetWorldResponse { world: Some(world) }; + Ok(tonic::Response::new(res)) + } +} diff --git a/server/src/world/impl.rs b/server/src/world/impl.rs new file mode 100644 index 0000000..a539dc5 --- /dev/null +++ b/server/src/world/impl.rs @@ -0,0 +1,34 @@ +use futures::{future, FutureExt}; + +impl super::WorldService for super::WorldServiceImpl +where + Context: super::WorldSizeStore, +{ + type Error = super::Error; + + fn get_world_size<'a>( + &'a self, + ctx: &'a Context, + req: super::GetWorldSize, + ) -> future::BoxFuture<'a, Result> { + let super::GetWorldSize {} = req; + let size = ctx.world_size(); + let fut = future::ready(Ok(size)); + fut.boxed() + } + + fn check_coordinate<'a>( + &'a self, + ctx: &'a Context, + req: super::CheckCoordinate, + ) -> future::BoxFuture<'a, Result> { + let size = ctx.world_size(); + let super::CheckCoordinate { coordinate } = req; + let res = if coordinate.x < size.width && coordinate.y < size.height { + super::CheckCoordinateAnswer::Valid(coordinate) + } else { + super::CheckCoordinateAnswer::Invalid + }; + future::ready(Ok(res)).boxed() + } +}