use tonic::{transport::Server, Request, Response, Status}; use log::info; use std::sync::Arc; mod game; use game::connection_server::{Connection, ConnectionServer}; use game::lobby_server::{Lobby, LobbyServer}; use game::{LobbyCode, Null, UserId, Name, CardId, Image}; use crate::db; pub struct ConnectionService { conn: Arc, properties: Arc } #[tonic::async_trait] impl Connection for ConnectionService { async fn connect(&self, request: Request) -> Result, Status> { let name = request.into_inner().name; let mut conn = self.conn.acquire().await; let uuid = conn.add_user(&name).await; info!("Connected {}[{}]", name, uuid); conn.close().await; Ok(Response::new(UserId { id: uuid.to_hyphenated().to_string(), })) } async fn join_lobby_with_code( &self, request: Request, ) -> Result, Status> { let uuid = client_id::get(request.metadata()).map_err(|x| match x { client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id") })?; let lobby = request.get_ref().code; let mut conn = self.conn.acquire().await; conn.join_lobby(uuid, lobby).await; conn.close().await; Ok(Response::new(Null{})) } async fn join_lobby_without_code( &self, request: Request, ) -> Result, Status> { let uuid = client_id::get(request.metadata()).map_err(|x| match x { client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id") })?; let mut conn = self.conn.acquire().await; let lobby = conn.create_lobby().await; conn.join_lobby(uuid, lobby).await; conn.close().await; Ok(Response::new(LobbyCode{code: lobby})) } async fn name( &self, _request: Request, ) -> Result, Status> { Ok(Response::new(game::Name {name: self.properties.name.clone() })) } } use tokio::sync::mpsc; pub struct LobbyService { conn: Arc, games: Arc>, } #[tonic::async_trait] impl Lobby for LobbyService { type getGamesStream = mpsc::UnboundedReceiver>; async fn get_games( &self, _: Request, ) -> Result, Status> { let (sender, receiver) = mpsc::unbounded_channel(); for (id, game) in self.games.iter().enumerate() { sender.send(Ok(game::Game {id: id as u64, name: game.name.clone(), version: game.version.clone(), authors: game.authors.clone()})).unwrap(); } Ok(Response::new(receiver)) } async fn get_card_image(&self, request: tonic::Request) -> Result, tonic::Status> { let id = request.into_inner(); let game = &self.games.as_ref()[id.game_id as usize]; let card = game.get_card_path(&id.card_id); let mut buffer = Vec::new(); image::open(&card).expect(&format!("Error loading the image in {:?}", card)).write_to(&mut buffer, image::ImageOutputFormat::Png).unwrap(); Ok(Response::new(Image {content: buffer})) } async fn vote( &self, request: Request, ) -> Result, Status> { todo!() } async fn ready( &self, request: Request, ) -> Result, Status> { todo!() } async fn status( &self, request: Request, ) -> Result, Status> { todo!() } } pub async fn start(pool: db::DbPool, games: Vec, properties: crate::server_properties::ServerProperties) { let arc = Arc::new(pool); let properties = Arc::new(properties); let connection = ConnectionService { conn: arc.clone(), properties }; let lobby = LobbyService {conn: arc.clone(), games: Arc::new(games)}; Server::builder() .add_service(ConnectionServer::new(connection)) .add_service(LobbyServer::new(lobby)) .serve("0.0.0.0:50052".parse().unwrap()) .await .unwrap(); } mod client_id { pub fn get( metadata: &tonic::metadata::MetadataMap, ) -> Result { metadata .get("client_id") .ok_or(Error::NotSet) .and_then(|x| { uuid::Uuid::parse_str(x.to_str().map_err(|_| Error::MalformedUuid)?) .map_err(|_| Error::MalformedUuid) }) } pub enum Error { MalformedUuid, NotSet, } }