redlib icon indicating copy to clipboard operation
redlib copied to clipboard

Refactor: idiomatic `Result` types

Open Tokarak opened this issue 1 year ago • 1 comments

  • Throughout the project, all errors are coerced into Strings. This is very bad practice because:
    • There is no option to handle the errors smoothly — display the error to the client, log it, that's it.
    • No error propagation with the try operator (?); the whole project is filled with boilerplate match statements.
    • Untyped rust is painful to work with.
  • The type of BoxResponse: type BoxResponse = Pin<Box<dyn Future<Output = Result<Response<Body>, String>> + Send>>; means that futures that return results with generic errors are a primitive in the program. The result is things like this:
async fn pwa_logo() -> Result<Response<Body>, String> {
	Ok(
		Response::builder()
			.status(200)
			.header("content-type", "image/png")
			.body(include_bytes!("../static/logo.png").as_ref().into())
			.unwrap_or_default(),
	)
}

http::Error::Result<Response<Body>> is obviously the intended return type. Even better, since the function always returns Ok(_), it should return Response<Body>, however

This refactoring is crucial for readability and productivity. Hopefully, the only errors are the http error, hyper error, and the custom errors which are intended to be rendered to the client by the error boilerplate.

Tokarak avatar Feb 06 '24 23:02 Tokarak


use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use std::convert::Infallible;
use std::net::SocketAddr;

async fn handle_request(req: Request<Body>) -> Result<Response<Body>, AppError> {
    match req.uri().path() {
        "/logo" => Ok(pwa_logo()), // Call the infallible version
        _ => Err(AppError::Custom("Not Found".to_string())),
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    // Create a service that handles requests
    let make_svc = make_service_fn(|_conn| async {
        Ok::<_, Infallible>(service_fn(|req| async {
            handle_request(req)
                .await
                .map_err(|e| e.into_response()) // Convert errors to responses
        }))
    });

    let server = Server::bind(&addr).serve(make_svc);
    println!("Server running on http://{}", addr);
    server.await?;
    Ok(())
}

ljluestc avatar Aug 17 '25 16:08 ljluestc