Isolate error handling from Warp

This commit is contained in:
Savanni D'Gerinel 2024-11-21 18:46:05 -05:00
parent 5535632466
commit 0f42ebcc30
5 changed files with 118 additions and 56 deletions

View File

@ -1,14 +1,32 @@
use std::{
collections::{hash_map::Iter, HashMap},
fmt::{self, Display},
io::Read,
};
use thiserror::Error;
#[derive(Debug, Error)]
enum Error {
#[error("Asset could not be found: {0}")]
AssetNotFound(AssetId),
pub enum Error {
#[error("Asset could not be found")]
NotFound,
#[error("Asset could not be opened")]
Inaccessible,
#[error("An unexpected IO error occured when retrieving an asset {0}")]
UnexpectedError(std::io::Error),
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
use std::io::ErrorKind::*;
match err.kind() {
NotFound => Error::NotFound,
PermissionDenied | UnexpectedEof => Error::Inaccessible,
_ => Error::UnexpectedError(err),
}
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
@ -70,7 +88,14 @@ impl Assets for FsAssets {
}
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> {
unimplemented!()
let path = match self.assets.get(&asset_id) {
Some(asset) => Ok(asset),
None => Err(Error::NotFound),
}?;
let mut content: Vec<u8> = Vec::new();
let mut file = std::fs::File::open(&path)?;
file.read_to_end(&mut content)?;
Ok(content)
}
}

View File

@ -10,7 +10,7 @@ use urlencoding::decode;
use uuid::Uuid;
use crate::{
asset_db::{AssetId, Assets},
asset_db::{self, AssetId, Assets},
types::{AppError, Message, Tabletop, RGB},
};
@ -82,19 +82,14 @@ impl Core {
self.0.read().unwrap().tabletop.clone()
}
pub async fn get_file(&self, file_name: String) -> Vec<u8> {
/*
let file_name = decode(&file_name).expect("UTF-8");
let mut full_path = self.0.read().unwrap().image_base.clone();
full_path.push(file_name.to_string());
let mut content: Vec<u8> = Vec::new();
let mut file = std::fs::File::open(&full_path).unwrap();
file.read_to_end(&mut content).unwrap();
content
*/
unimplemented!()
pub async fn get_asset(&self, asset_id: AssetId) -> Result<Vec<u8>, AppError> {
self.0.read().unwrap().asset_db.get(asset_id.clone()).map_err(|err| {
match err {
asset_db::Error::NotFound => AppError::NotFound(format!("{}", asset_id)),
asset_db::Error::Inaccessible => AppError::Inaccessible(format!("{}", asset_id)),
asset_db::Error::UnexpectedError(err) => AppError::Inaccessible(format!("{}", err)),
}
})
}
pub fn available_images(&self) -> Vec<AssetId> {

View File

@ -1,8 +1,10 @@
use std::future::Future;
use futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use warp::{http::Response, reply::Reply, ws::Message};
use warp::{http::Response, http::StatusCode, reply::Reply, ws::Message};
use crate::core::Core;
use crate::{asset_db::AssetId, core::Core, types::AppError};
/*
pub async fn handle_auth(
@ -28,24 +30,52 @@ pub async fn handle_auth(
}
*/
pub async fn handle_file(core: Core, file_name: String) -> impl Reply {
let mimetype = mime_guess::from_path(&file_name).first().unwrap();
let bytes = core.get_file(file_name).await;
Response::builder()
pub async fn handler<F>(f: F) -> impl Reply
where
F: Future<Output = Result<Response<Vec<u8>>, AppError>>,
{
match f.await {
Ok(response) => response,
Err(AppError::NotFound(_)) => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(vec![])
.unwrap(),
Err(_) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(vec![])
.unwrap(),
}
}
pub async fn handle_file(core: Core, asset_id: AssetId) -> impl Reply {
handler(async move {
let mimetype = mime_guess::from_path(&format!("{}", asset_id))
.first()
.unwrap();
let bytes = core.get_asset(asset_id).await?;
Ok(Response::builder()
.header("application-type", mimetype.to_string())
.body(bytes)
.unwrap()
.unwrap())
})
.await
}
pub async fn handle_available_images(core: Core) -> impl Reply {
let image_paths: Vec<String> = core.available_images().into_iter()
.map(|path| format!("{}", path)).collect();
handler(async move {
let image_paths: Vec<String> = core
.available_images()
.into_iter()
.map(|path| format!("{}", path))
.collect();
Response::builder()
Ok(Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Content-Type", "application/json")
.body(serde_json::to_string(&image_paths).unwrap())
.unwrap()
.body(serde_json::to_vec(&image_paths).unwrap())
.unwrap())
})
.await
}
#[derive(Deserialize, Serialize)]
@ -57,24 +87,30 @@ pub struct RegisterResponse {
}
pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply {
handler(async move {
let client_id = core.register_client();
Response::builder()
Ok(Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Content-Type", "application/json")
.body(
serde_json::to_string(&RegisterResponse {
serde_json::to_vec(&RegisterResponse {
url: format!("ws://127.0.0.1:8001/ws/{}", client_id),
})
.unwrap(),
)
.unwrap()
.unwrap())
})
.await
}
pub async fn handle_unregister_client(core: Core, client_id: String) -> impl Reply {
handler(async move {
core.unregister_client(client_id);
warp::reply::reply()
Ok(Response::builder().status(StatusCode::NO_CONTENT).body(vec![]).unwrap())
})
.await
}
pub async fn handle_connect_websocket(
@ -109,12 +145,15 @@ pub async fn handle_connect_websocket(
}
pub async fn handle_set_background_image(core: Core, image_name: String) -> impl Reply {
handler(async move {
let _ = core.set_background_image(image_name);
Response::builder()
Ok(Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*")
.header("Content-Type", "application/json")
.body("")
.unwrap()
.body(vec![])
.unwrap())
})
.await
}

View File

@ -1,4 +1,4 @@
use asset_db::FsAssets;
use asset_db::{AssetId, FsAssets};
use authdb::AuthError;
use handlers::{
handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest
@ -103,7 +103,7 @@ pub async fn main() {
.and(warp::get())
.then({
let core = core.clone();
move |file_name| handle_file(core.clone(), file_name)
move |file_name| handle_file(core.clone(), AssetId::from(file_name))
});
let route_available_images = warp::path!("api" / "v1" / "image").and(warp::get()).then({

View File

@ -1,10 +1,13 @@
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[derive(Debug)]
pub enum AppError {
NotFound(String),
Inaccessible(String),
JsonError(serde_json::Error),
UnexpectedError(String),
}
#[derive(Clone, Debug, Deserialize, Serialize)]