use std::future; use futures::{StreamExt, TryStreamExt}; use rocket_db_pools::mongodb::bson::to_bson; use super::{prelude::*, util::PartyError}; use lan_party_core::event::*; api_routes!( create_event, stop_event, get_event, update_event, get_all_events, event_outcome, delete_event, ); pub async fn apply_outcome(outcome: &EventOutcome, db: &Connection) -> Result<(), PartyError> { for (player, reward) in outcome.points.iter() { db.users() .update_one( doc! { "name": player.to_string() }, doc! { "$inc": { "score": reward } }, None, ) .await?; } Ok(()) } /// # Create event /// /// Returns the created event. #[openapi(tag = "Event")] #[post("/", data = "")] pub async fn create_event( _api_key: ApiKey, db: Connection, event: Json, ) -> Result, PartyError> { let event = event.into_inner().create_event()?; db.events().insert_one(&event, None).await?; Ok(Json(event)) } /// # Update event /// /// Update the supplied values in the event with the given name. The `name` of an event cannot be /// changed and `concluded` will always be false. #[openapi(tag = "Event")] #[post("/", data = "")] pub async fn update_event( _api_key: ApiKey, db: Connection, name: String, update: Json, ) -> Result { let mut event = db .events() .find_one(doc! { "name": &name }, None) .await? .ok_or(PartyError::EventNotFound(name.clone()))?; event.apply_update(update.into_inner())?; db.events() .update_one( doc! { "name": &name }, doc! { "$set": to_bson(&event)? }, None, ) .await?; Ok(Status::Ok) } /// # Get event /// /// Returns the event with the given name #[openapi(tag = "Event")] #[get("/")] pub async fn get_event( _api_key: ApiKey, db: Connection, name: String, ) -> Result, PartyError> { Ok(Json( db.events() .find_one(doc! { "name": &name }, None) .await? .ok_or(PartyError::EventNotFound(name))?, )) } /// # Get events /// /// Returns all events #[openapi(tag = "Event")] #[get("/?")] pub async fn get_all_events( _api_key: ApiKey, db: Connection, concluded: Option, ) -> Result>, PartyError> { let filter = if let Some(concluded) = concluded { doc! { "concluded": concluded } } else { doc! {} }; Ok(Json( db.events() .find(filter, None) .await? .filter(|e| future::ready(e.is_ok())) .try_collect() .await?, )) } /// # Get outcome /// /// Returns the outcome of an event. #[openapi(tag = "Event")] #[get("//outcome")] pub async fn event_outcome( _api_key: ApiKey, db: Connection, name: String, ) -> Result, PartyError> { let event = db .events() .find_one(doc! { "name": &name }, None) .await? .ok_or(PartyError::EventNotFound(name.clone()))?; Ok(Json(event.outcome())) } /// # Stop event /// /// This will conclude the event, apply the outcome to the users and return the outcome. #[openapi(tag = "Event")] #[post("//stop")] pub async fn stop_event( _api_key: ApiKey, db: Connection, name: String, ) -> Result, PartyError> { let event = db .events() .find_one(doc! { "name": &name }, None) .await? .ok_or(PartyError::EventNotFound(name.clone()))?; event.conclude(); db.events() .update_one( doc! { "name": &name }, doc! { "$set": { "concluded": true } }, None, ) .await?; let outcome = event.outcome(); apply_outcome(&outcome, &db).await?; Ok(Json(outcome)) } /// # Delete event by name #[openapi(tag = "Event")] #[delete("/")] pub async fn delete_event( _api_key: ApiKey, db: Connection, name: String, ) -> Result { db.events().delete_one(doc! { "name": name }, None).await?; Ok(Status::Ok) }