feat: Implement CachedOption for get_game_thumbnail response, trim game titles, and update toolchain to nightly.
This commit is contained in:
parent
70e1a555c0
commit
1ff7e1dd83
65
backend/src/cached_option.rs
Normal file
65
backend/src/cached_option.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
2
rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
Loading…
x
Reference in New Issue
Block a user