Skip to content

Commit

Permalink
feat: add server method filters (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
cakekindel authored Feb 7, 2023
1 parent 443ece5 commit 0ec28c7
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions toad/examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use toad::net::Addrd;
use toad::platform::Platform as _;
use toad::req::Req;
use toad::server::ap::state::{Complete, Hydrated};
use toad::server::{path, respond, Ap, BlockingServer, Init};
use toad::server::{method, path, respond, Ap, BlockingServer, Init};
use toad::std::{dtls, Platform, PlatformTypes as T};
use toad::step::runtime;

Expand Down Expand Up @@ -50,13 +50,15 @@ mod route {
-> Ap<Complete, T<dtls::N>, (), io::Error> {
#![allow(unreachable_code)]

ap.pipe(path::check::rest_equals("done"))
ap.pipe(method::post)
.pipe(path::check::rest_equals("done"))
.bind(|_| Ap::respond(panic!("shutting down...")))
}

pub fn hello(ap: Ap<Hydrated, T<dtls::N>, (), io::Error>)
-> Ap<Complete, T<dtls::N>, (), io::Error> {
ap.pipe(path::segment::check::next_equals("hello"))
ap.pipe(method::get)
.pipe(path::segment::check::next_equals("hello"))
.pipe(path::segment::next(|_, name| {
name.map(String::from)
.map(Ap::ok)
Expand Down Expand Up @@ -114,7 +116,7 @@ pub fn main() {
test::hello(&client, server_addr);
test::not_found(&client, server_addr);

client.send_msg(Addrd(Req::<T<dtls::N>>::get("done").into(),
client.send_msg(Addrd(Req::<T<dtls::N>>::post("done").into(),
server_addr.parse().unwrap()))
.unwrap();
log::info!("[6] done");
Expand Down
13 changes: 13 additions & 0 deletions toad/src/req/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ use crate::code;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Method(pub(super) Code);

impl Default for Method {
fn default() -> Self {
Self::GET
}
}

impl Method {
/// Convert this into a [`Code`]
pub fn code(self) -> Code {
self.0
}
}

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl core::fmt::Display for Method {
Expand Down
84 changes: 84 additions & 0 deletions toad/src/server/method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use tinyvec::{array_vec, ArrayVec};

use super::ap::state::Hydrated;
use super::ap::Hydrate;
use super::Ap;
use crate::platform::PlatformTypes;
use crate::req::Method;

/// Reject request if the code does not match `method`
pub fn is<P, T, E>(method: Method) -> impl Fn(Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
is_one_of(array_vec! {_ => method})
}

/// Reject request if the code is not included in `methods`
pub fn is_one_of<P, T, E>(methods: ArrayVec<[Method; 5]>)
-> impl Fn(Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
move |ap| match ap.try_unwrap_ok_hydrated() {
| Ok((t, h))
if methods.iter()
.any(|m| m.code() == h.req.data().as_ref().code) =>
{
Ap::ok_hydrated(t, h)
},
| Ok((_, Hydrate { req, .. })) => Ap::reject_hydrated(req).pretend(),
| Err(e) => e,
}
}

/// Reject requests that aren't [`GET`](Method::GET) requests
///
/// ```
/// use toad_msg::{Type, Message, Id, Token};
/// use toad::server::{ap::{Ap, Hydrate}, method};
/// use toad::std::{PlatformTypes as P, dtls};
/// use toad::net::Addrd;
/// use toad::req::{Req, Method};
/// # let msg = |m: Method| Message::new(Type::Con, m.code(), Id(0), Token(Default::default()));
/// # let req = |m: Method| Addrd(Req::from(msg(m)), "0.0.0.0:1234".parse().unwrap());
/// # let ap = |m: Method| Ap::ok_hydrated((), Hydrate::from_request(req(m)));
///
/// let get_request: Ap<_, P<dtls::Y>, (), ()> = /* ... */
/// # ap(Method::GET);
/// assert!(get_request.pipe(method::get).is_ok());
///
/// let post_request: Ap<_, P<dtls::Y>, (), ()> = /* ... */
/// # ap(Method::POST);
/// assert!(post_request.pipe(method::get).is_rejected());
/// ```
pub fn get<P, T, E>(ap: Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
ap.pipe(is(Method::GET))
}

/// Reject non-POST requests
pub fn post<P, T, E>(ap: Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
ap.pipe(is(Method::POST))
}

/// Reject non-PUT requests
pub fn put<P, T, E>(ap: Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
ap.pipe(is(Method::PUT))
}

/// Reject non-DELETE requests
pub fn delete<P, T, E>(ap: Ap<Hydrated, P, T, E>) -> Ap<Hydrated, P, T, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
ap.pipe(is(Method::DELETE))
}
3 changes: 3 additions & 0 deletions toad/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pub mod ap;
/// * [`ends_with()`](path::check::ends_with) - assert that the rest of the route ends with a string
pub mod path;

/// Request method filters
pub mod method;

/// Respond to requests
pub mod respond;

Expand Down

0 comments on commit 0ec28c7

Please sign in to comment.