Skip to content

Instantly share code, notes, and snippets.

@romac
Created March 15, 2019 12:34
Show Gist options
  • Save romac/012d78e0a2b21737ef50129826008614 to your computer and use it in GitHub Desktop.
Save romac/012d78e0a2b21737ef50129826008614 to your computer and use it in GitHub Desktop.
Rocket + frunk Coproduct for type-safe route results
type TestRes = RouteResult<
Created<JsonValue>,
Coprod!(
BadRequest<JsonValue>,
NotFound<JsonValue>,
InternalServerError<JsonValue>
),
>;
#[post("/test/<a>")]
pub fn test(a: u32) -> TestRes {
if a < 0 {
return Route::err(BadRequest(JsonValue(json!({
"status": "error",
"type": "Negative number",
}))));
}
if a == 0 {
return Route::err(NotFound(JsonValue(json!({
"status": "error",
"type": "NotFound",
"reason": "Not found with a = 0",
}))));
}
if a != 42 {
return RouteResult::err(InternalServerError(JsonValue(json!({
"status": "error",
"type": "Not42",
"reason": "Not equal 42" }))));
}
RouteResult::ok(Created(
uri!("/view", view: a = a).to_string(),
Some(JsonValue(json!({
"status": "success",
"a": a,
}))),
))
}
use std::result::Result as StdResult;
use frunk::coproduct::*;
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{Responder, Response, Result};
pub struct Route<A, B>(StdResult<A, RouteErr<B>>);
impl<A, B> Route<A, B> {
pub fn ok(a: A) -> Self {
Self(Ok(a))
}
pub fn err<T, I>(i: I) -> Self
where
B: CoprodInjector<I, T>,
{
Self(Err(RouteErr::new(i)))
}
}
impl<'r, A, B> Responder<'r> for Route<A, B>
where
A: Responder<'r>,
RouteErr<B>: Responder<'r>,
{
fn respond_to(self, request: &Request) -> Result<'r> {
match self.0 {
Ok(a) => a.respond_to(request),
Err(b) => b.respond_to(request),
}
}
}
pub struct RouteErr<E>(pub E);
impl<E> RouteErr<E> {
pub fn new<T, I>(t: T) -> Self
where
E: CoprodInjector<T, I>,
{
RouteErr(E::inject(t))
}
}
impl<'r> Responder<'r> for RouteErr<CNil> {
fn respond_to(self, request: &Request) -> Result<'r> {
InternalServerError::<JsonValue>(None).respond_to(request)
}
}
impl<'r, H, T> Responder<'r> for RouteErr<Coproduct<H, T>>
where
H: Responder<'r>,
RouteErr<T>: Responder<'r>,
{
fn respond_to(self, request: &Request) -> Result<'r> {
match self.0 {
Coproduct::Inl(l) => l.respond_to(request),
Coproduct::Inr(r) => RouteErr(r).respond_to(request),
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment