feat: Implement CachedOption for get_game_thumbnail response, trim game titles, and update toolchain to nightly.

This commit is contained in:
code002lover 2025-12-04 16:12:47 +01:00
parent 70e1a555c0
commit 1ff7e1dd83
4 changed files with 84 additions and 11 deletions

View File

@ -0,0 +1,65 @@
use rocket::http::Status;
use rocket::response::Responder;
use rocket::{Request, Response, response};
use std::ops::{FromResidual, Try};
#[derive(Debug)]
pub struct CachedOption(Option<(rocket::http::ContentType, Vec<u8>)>);
impl CachedOption {
fn into_option(self) -> Option<(rocket::http::ContentType, Vec<u8>)> {
self.0
}
}
impl From<Option<(rocket::http::ContentType, Vec<u8>)>> for CachedOption {
fn from(value: Option<(rocket::http::ContentType, Vec<u8>)>) -> Self {
Self(value)
}
}
impl FromResidual<std::option::Option<std::convert::Infallible>> for CachedOption {
fn from_residual(_: std::option::Option<std::convert::Infallible>) -> Self {
Self(None)
}
}
impl FromResidual<CachedOption> for CachedOption {
fn from_residual(residual: CachedOption) -> Self {
residual
}
}
impl Try for CachedOption {
type Output = Option<(rocket::http::ContentType, Vec<u8>)>;
type Residual = Self;
fn from_output(output: Self::Output) -> Self {
Self(output)
}
fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
match self.0 {
Some((ct, bytes)) => std::ops::ControlFlow::Break(Self(Some((ct, bytes)))),
None => std::ops::ControlFlow::Continue(None),
}
}
}
impl<'r> Responder<'r, 'static> for CachedOption {
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
let option = self.into_option();
if let Some((ct, bytes)) = option {
Response::build()
.header(ct)
.header(rocket::http::Header::new(
"Cache-Control",
"max-age=31536000",
))
.sized_body(bytes.len(), std::io::Cursor::new(bytes))
.ok()
} else {
Err(Status::InternalServerError)
}
}
}

View File

@ -1,3 +1,4 @@
#![feature(try_trait_v2)]
use rocket::fs::FileServer; use rocket::fs::FileServer;
use rocket::futures::lock::Mutex; use rocket::futures::lock::Mutex;
@ -62,12 +63,14 @@ async fn add_game(
game: proto_utils::Proto<items::Game>, game: proto_utils::Proto<items::Game>,
) -> Option<items::Game> { ) -> Option<items::Game> {
let mut games = game_list.lock().await; let mut games = game_list.lock().await;
let game = game.into_inner(); let mut game = game.into_inner();
if games.iter().any(|g| g.title == game.title) { if games.iter().any(|g| g.title == game.title) {
return None; return None;
} }
game.title = game.title.trim().to_string();
games.push(game.clone()); games.push(game.clone());
let users = user_list.lock().await; let users = user_list.lock().await;
@ -118,11 +121,14 @@ async fn add_opinion(
result result
} }
mod cached_option;
use cached_option::CachedOption;
#[get("/game_thumbnail/<title>")] #[get("/game_thumbnail/<title>")]
async fn get_game_thumbnail( async fn get_game_thumbnail(
title: &str, title: &str,
game_list: &rocket::State<Mutex<Vec<Game>>>, game_list: &rocket::State<Mutex<Vec<Game>>>,
) -> Option<(rocket::http::ContentType, Vec<u8>)> { ) -> CachedOption {
// 1. Sanitize title for filename // 1. Sanitize title for filename
let safe_title: String = title let safe_title: String = title
.chars() .chars()
@ -140,7 +146,7 @@ async fn get_game_thumbnail(
std::fs::read_to_string(&cache_path_type), std::fs::read_to_string(&cache_path_type),
) && let Some(ct) = rocket::http::ContentType::parse_flexible(&type_str) ) && let Some(ct) = rocket::http::ContentType::parse_flexible(&type_str)
{ {
return Some((ct, bytes)); return Some((ct, bytes)).into();
} }
let games = game_list.lock().await; let games = game_list.lock().await;
@ -177,10 +183,10 @@ async fn get_game_thumbnail(
if let Ok(json) = resp.json::<serde_json::Value>().await { if let Ok(json) = resp.json::<serde_json::Value>().await {
json["data"][0]["imageUrl"].as_str()?.to_string() json["data"][0]["imageUrl"].as_str()?.to_string()
} else { } else {
return None; return None.into();
} }
} }
Err(_) => return None, Err(_) => return None.into(),
} }
} }
}; };
@ -199,9 +205,9 @@ async fn get_game_thumbnail(
let _ = std::fs::write(&cache_path_bin, &bytes); let _ = std::fs::write(&cache_path_bin, &bytes);
let _ = std::fs::write(&cache_path_type, content_type.to_string()); let _ = std::fs::write(&cache_path_type, content_type.to_string());
Some((content_type, bytes)) Some((content_type, bytes)).into()
} }
Err(_) => None, Err(_) => None.into(),
} }
} }

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@ -9,7 +9,7 @@
"remote_id": 98626216952426 "remote_id": 98626216952426
}, },
{ {
"title": " Asylum Life", "title": "Asylum Life",
"source": 1, "source": 1,
"min_players": 1, "min_players": 1,
"max_players": 40, "max_players": 40,
@ -43,7 +43,7 @@
"name": "Code002Lover", "name": "Code002Lover",
"opinion": [ "opinion": [
{ {
"title": " Asylum Life", "title": "Asylum Life",
"would_play": true "would_play": true
}, },
{ {
@ -63,7 +63,7 @@
"would_play": true "would_play": true
}, },
{ {
"title": " Asylum Life", "title": "Asylum Life",
"would_play": false "would_play": false
}, },
{ {