Added an error type that can convert into axum responses.
This commit is contained in:
52
src/error.rs
Normal file
52
src/error.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Just a way to unify error handling. Can translate eyre Reports into
|
||||
// axum responses.
|
||||
|
||||
use axum::{http::StatusCode, response::IntoResponse};
|
||||
use color_eyre::eyre::Report;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GBError {
|
||||
status: StatusCode,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl GBError {
|
||||
pub fn new(status: StatusCode, message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
status,
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn internal(err: impl Display) -> Self {
|
||||
Self::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
|
||||
}
|
||||
|
||||
pub fn not_found(err: impl Display) -> Self {
|
||||
Self::new(StatusCode::NOT_FOUND, err.to_string())
|
||||
}
|
||||
|
||||
pub fn timeout(err: impl Display) -> Self {
|
||||
Self::new(StatusCode::REQUEST_TIMEOUT, err.to_string())
|
||||
}
|
||||
|
||||
pub fn unavailable(err: impl Display) -> Self {
|
||||
Self::new(StatusCode::SERVICE_UNAVAILABLE, err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for GBError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
(self.status, self.message).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<E> for GBError
|
||||
where
|
||||
E: Into<Report> + Display,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
Self::internal(err)
|
||||
}
|
||||
}
|
||||
40
src/main.rs
40
src/main.rs
@@ -4,13 +4,13 @@
|
||||
use axum::{
|
||||
BoxError, Router,
|
||||
error_handling::HandleErrorLayer,
|
||||
extract::{DefaultBodyLimit, Multipart, Path, State, rejection::RawFormRejection},
|
||||
extract::{DefaultBodyLimit, Multipart, Path, State},
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse, Response},
|
||||
routing::{get, post},
|
||||
};
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::Result;
|
||||
use crate::error::GBError;
|
||||
use nanoid::nanoid;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use store::Store;
|
||||
@@ -19,6 +19,7 @@ use tower::{ServiceBuilder, buffer::BufferLayer, limit::rate::RateLimitLayer};
|
||||
use tower_http::{compression::CompressionLayer, limit::RequestBodyLimitLayer, trace::TraceLayer};
|
||||
use tracing::{Level, event};
|
||||
|
||||
mod error;
|
||||
mod store;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -51,31 +52,21 @@ struct Args {
|
||||
}
|
||||
|
||||
// Should just display some default page.
|
||||
async fn default_handler() -> Response {
|
||||
async fn default_handler() -> Result<Html<String>, GBError> {
|
||||
match fs::read_to_string("index.html").await {
|
||||
Ok(content) => Html(content).into_response(),
|
||||
Err(e) => (
|
||||
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to read index.html: {}", e),
|
||||
)
|
||||
.into_response(),
|
||||
Ok(content) => Ok(Html(content)),
|
||||
Err(err) => Err(GBError::internal(err)),
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Anything that reaches this is pretty much unrecoverable.
|
||||
async fn handle_error(err: BoxError) -> (StatusCode, String) {
|
||||
async fn handle_error(err: BoxError) -> GBError {
|
||||
if err.is::<tower::timeout::error::Elapsed>() {
|
||||
(StatusCode::REQUEST_TIMEOUT, "Request timed out".to_string())
|
||||
GBError::timeout(err)
|
||||
} else if err.is::<tower::load_shed::error::Overloaded>() {
|
||||
(
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
"Service overloaded, try again later".to_string(),
|
||||
)
|
||||
GBError::unavailable(err)
|
||||
} else {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Internal error: {}", err),
|
||||
)
|
||||
GBError::internal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +77,10 @@ async fn handle_error(err: BoxError) -> (StatusCode, String) {
|
||||
//
|
||||
// Response is the id assigned to the paste. Might do a redirect
|
||||
// to the url in the future.
|
||||
async fn upload_handler(State(mut state): State<Store>, mut multipart: Multipart) -> Response {
|
||||
async fn upload_handler(
|
||||
State(mut state): State<Store>,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<String, GBError> {
|
||||
while let Ok(Some(part)) = multipart.next_field().await {
|
||||
if let Some(name) = part.name()
|
||||
&& name == "file"
|
||||
@@ -94,11 +88,11 @@ async fn upload_handler(State(mut state): State<Store>, mut multipart: Multipart
|
||||
let id = nanoid!();
|
||||
let data = part.bytes().await.unwrap();
|
||||
let _ = state.put(id.clone(), data).await;
|
||||
return (StatusCode::OK, id).into_response();
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
|
||||
(StatusCode::OK, nanoid!()).into_response()
|
||||
Err(GBError::internal("Error with upload."))
|
||||
}
|
||||
|
||||
// Fetch a paste based on id in the path.
|
||||
@@ -129,7 +123,7 @@ fn install_tracing() {
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
async fn main() -> Result<(), GBError> {
|
||||
install_tracing();
|
||||
|
||||
color_eyre::install()?;
|
||||
|
||||
Reference in New Issue
Block a user