/* Hurry Curry! - a game about cooking Copyright (C) 2025 Hurry Curry! Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::Registry; use anyhow::Result; use hurrycurry_protocol::registry::Entry; use rocket::{ get, http::{Header, MediaType}, request::{self, FromRequest, Outcome}, response::{ self, content::{RawHtml, RawJson}, Responder, }, Either, Request, State, }; use std::sync::Arc; use tokio::sync::RwLock; #[get("/v1/list")] pub(super) async fn r_list( registry: &State>>, json: AcceptJson, ) -> Cors>, RawHtml>>> { Cors(if json.0 { Either::Left(RawJson(registry.read().await.json_response.clone())) } else { Either::Right(RawHtml(registry.read().await.html_response.clone())) }) } pub(super) fn generate_json_list(entries: &[Entry]) -> Result> { Ok(serde_json::to_string(&entries)?.into()) } pub(super) fn generate_html_list(entries: &[Entry]) -> Result> { Ok(ListPage { entries }.to_string().into()) } markup::define!( ListPage<'a>(entries: &'a [Entry]) { @markup::doctype() html { head { title { "Hurry Curry! Server Registry" } } body { table { tr { th {"Server"} th { "Players" } th { "Protocol" } } @for e in *entries { tr { td { details { summary { @e.name } ul { @for a in &e.address { li { @a } }} } } td { @e.players_online } td { @e.version.0 "." @e.version.1 } }} } } } } ); pub struct AcceptJson(bool); impl<'r> FromRequest<'r> for AcceptJson { type Error = (); fn from_request<'life0, 'async_trait>( request: &'r Request<'life0>, ) -> ::core::pin::Pin< Box< dyn ::core::future::Future> + ::core::marker::Send + 'async_trait, >, > where 'r: 'async_trait, 'life0: 'async_trait, Self: 'async_trait, { Box::pin(async move { Outcome::Success(AcceptJson( request .accept() .map(|a| a.preferred().exact_eq(&MediaType::JSON)) .unwrap_or(false), )) }) } } pub struct Cors(pub T); #[rocket::async_trait] impl<'r, T: Responder<'r, 'static>> Responder<'r, 'static> for Cors { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let mut b = self.0.respond_to(req)?; b.set_header(Header::new("access-control-allow-origin", "*")); Ok(b) } }