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::{ use std::{
collections::{hash_map::Iter, HashMap}, collections::{hash_map::Iter, HashMap},
fmt::{self, Display}, fmt::{self, Display},
io::Read,
}; };
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
enum Error { pub enum Error {
#[error("Asset could not be found: {0}")] #[error("Asset could not be found")]
AssetNotFound(AssetId), 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)] #[derive(Clone, Debug, Hash, Eq, PartialEq)]
@ -70,7 +88,14 @@ impl Assets for FsAssets {
} }
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> { 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 uuid::Uuid;
use crate::{ use crate::{
asset_db::{AssetId, Assets}, asset_db::{self, AssetId, Assets},
types::{AppError, Message, Tabletop, RGB}, types::{AppError, Message, Tabletop, RGB},
}; };
@ -82,19 +82,14 @@ impl Core {
self.0.read().unwrap().tabletop.clone() self.0.read().unwrap().tabletop.clone()
} }
pub async fn get_file(&self, file_name: String) -> Vec<u8> { 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| {
let file_name = decode(&file_name).expect("UTF-8"); match err {
asset_db::Error::NotFound => AppError::NotFound(format!("{}", asset_id)),
let mut full_path = self.0.read().unwrap().image_base.clone(); asset_db::Error::Inaccessible => AppError::Inaccessible(format!("{}", asset_id)),
full_path.push(file_name.to_string()); asset_db::Error::UnexpectedError(err) => AppError::Inaccessible(format!("{}", err)),
}
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 fn available_images(&self) -> Vec<AssetId> { pub fn available_images(&self) -> Vec<AssetId> {

View File

@ -1,8 +1,10 @@
use std::future::Future;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize}; 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( 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 { pub async fn handler<F>(f: F) -> impl Reply
let mimetype = mime_guess::from_path(&file_name).first().unwrap(); where
let bytes = core.get_file(file_name).await; F: Future<Output = Result<Response<Vec<u8>>, AppError>>,
Response::builder() {
.header("application-type", mimetype.to_string()) match f.await {
.body(bytes) Ok(response) => response,
.unwrap() 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())
})
.await
} }
pub async fn handle_available_images(core: Core) -> impl Reply { pub async fn handle_available_images(core: Core) -> impl Reply {
let image_paths: Vec<String> = core.available_images().into_iter() handler(async move {
.map(|path| format!("{}", path)).collect(); 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("Access-Control-Allow-Origin", "*")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(serde_json::to_string(&image_paths).unwrap()) .body(serde_json::to_vec(&image_paths).unwrap())
.unwrap() .unwrap())
})
.await
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
@ -57,24 +87,30 @@ pub struct RegisterResponse {
} }
pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply { pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply {
let client_id = core.register_client(); handler(async move {
let client_id = core.register_client();
Response::builder() Ok(Response::builder()
.header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Origin", "*")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body( .body(
serde_json::to_string(&RegisterResponse { serde_json::to_vec(&RegisterResponse {
url: format!("ws://127.0.0.1:8001/ws/{}", client_id), url: format!("ws://127.0.0.1:8001/ws/{}", client_id),
}) })
.unwrap(), .unwrap(),
) )
.unwrap() .unwrap())
})
.await
} }
pub async fn handle_unregister_client(core: Core, client_id: String) -> impl Reply { pub async fn handle_unregister_client(core: Core, client_id: String) -> impl Reply {
core.unregister_client(client_id); 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( 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 { pub async fn handle_set_background_image(core: Core, image_name: String) -> impl Reply {
let _ = core.set_background_image(image_name); handler(async move {
let _ = core.set_background_image(image_name);
Response::builder() Ok(Response::builder()
.header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*") .header("Access-Control-Allow-Methods", "*")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body("") .body(vec![])
.unwrap() .unwrap())
})
.await
} }

View File

@ -1,4 +1,4 @@
use asset_db::FsAssets; use asset_db::{AssetId, FsAssets};
use authdb::AuthError; use authdb::AuthError;
use handlers::{ use handlers::{
handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest 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()) .and(warp::get())
.then({ .then({
let core = core.clone(); 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({ let route_available_images = warp::path!("api" / "v1" / "image").and(warp::get()).then({

View File

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