Skip to content

Commit

Permalink
Merge pull request fermyon#2013 from fermyon/try-into-request
Browse files Browse the repository at this point in the history
  • Loading branch information
rylev authored Nov 2, 2023
2 parents c2da208 + 6ca1be0 commit 1f7cc3f
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 18 deletions.
56 changes: 41 additions & 15 deletions sdk/rust/src/http/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use async_trait::async_trait;
use crate::wit::wasi::io::streams;

use super::{
Headers, IncomingRequest, IncomingResponse, OutgoingRequest, OutgoingResponse, RequestBuilder,
Headers, IncomingRequest, IncomingResponse, Json, JsonBodyError, OutgoingRequest,
OutgoingResponse, RequestBuilder,
};

use super::{responses, NonUtf8BodyError, Request, Response};
Expand Down Expand Up @@ -275,7 +276,7 @@ impl IntoResponse for Box<dyn std::error::Error> {
#[cfg(feature = "json")]
impl IntoResponse for super::JsonBodyError {
fn into_response(self) -> Response {
responses::bad_request(Some(format!("failed to parse JSON body: {}", self.0)))
responses::bad_request(Some(format!("invalid JSON body: {}", self.0)))
}
}

Expand Down Expand Up @@ -451,12 +452,10 @@ impl TryFromBody for String {
}

#[cfg(feature = "json")]
impl<T: serde::de::DeserializeOwned> TryFromBody for super::Json<T> {
type Error = super::JsonBodyError;
impl<T: serde::de::DeserializeOwned> TryFromBody for Json<T> {
type Error = JsonBodyError;
fn try_from_body(body: Vec<u8>) -> Result<Self, Self::Error> {
Ok(super::Json(
serde_json::from_slice(&body).map_err(super::JsonBodyError)?,
))
Ok(Json(serde_json::from_slice(&body).map_err(JsonBodyError)?))
}
}

Expand Down Expand Up @@ -502,6 +501,15 @@ where
}
}

#[cfg(feature = "json")]
impl<T: serde::Serialize> TryIntoBody for Json<T> {
type Error = JsonBodyError;

fn try_into_body(self) -> Result<Vec<u8>, Self::Error> {
serde_json::to_vec(&self.0).map_err(JsonBodyError)
}
}

/// A trait for converting a type into an `OutgoingRequest`
pub trait TryIntoOutgoingRequest {
/// The error if the conversion fails
Expand Down Expand Up @@ -633,14 +641,32 @@ impl<B: TryFromBody> TryFromIncomingResponse for hyperium::Response<B> {
}
}

/// Turn a type into a `Request`
pub trait TryIntoRequest {
/// The error if the conversion fails
type Error;

/// Turn `self` into a `Request`
fn try_into_request(self) -> Result<Request, Self::Error>;
}

impl TryIntoRequest for Request {
type Error = std::convert::Infallible;

fn try_into_request(self) -> Result<Request, Self::Error> {
Ok(self)
}
}

#[cfg(feature = "http")]
impl<B: IntoBody> From<hyperium::Request<B>> for Request {
fn from(value: hyperium::Request<B>) -> Self {
Request::builder()
.method(value.method().clone().into())
.uri(value.uri().to_string())
.headers(value.headers())
.body(value.into_body())
.build()
impl<B: TryIntoBody> TryIntoRequest for hyperium::Request<B> {
type Error = B::Error;
fn try_into_request(self) -> Result<Request, Self::Error> {
Ok(Request::builder()
.method(self.method().clone().into())
.uri(self.uri().to_string())
.headers(self.headers())
.body(B::try_into_body(self.into_body())?)
.build())
}
}
13 changes: 10 additions & 3 deletions sdk/rust/src/http/router.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::conversions::{IntoResponse, TryFromRequest};
use super::conversions::{IntoResponse, TryFromRequest, TryIntoRequest};
use super::{responses, Method, Request, Response};
use routefinder::{Captures, Router as MethodRouter};
use std::{collections::HashMap, fmt::Display};
Expand Down Expand Up @@ -39,8 +39,15 @@ struct RouteMatch<'a> {

impl Router {
/// Dispatches a request to the appropriate handler along with the URI parameters.
pub fn handle<R: Into<Request>>(&self, request: R) -> Response {
let request = request.into();
pub fn handle<R>(&self, request: R) -> Response
where
R: TryIntoRequest,
R::Error: IntoResponse,
{
let request = match R::try_into_request(request) {
Ok(r) => r,
Err(e) => return e.into_response(),
};
let method = request.method.clone();
let path = &request.path();
let RouteMatch { params, handler } = self.find(path, method);
Expand Down

0 comments on commit 1f7cc3f

Please sign in to comment.