Struct toad::server::ap::Ap

source ·
pub struct Ap<S, P, T, E>(_)
where
         S: ApState,
         P: PlatformTypes;
Expand description

Server Resources & Request-Response Semantics

Ap can be thought of as an extension of Result that adds new, servery, states.

Variants

  • Ok, Err - Result::Ok, Result::Err
  • OkHydrated - this is Result::Ok with a CoAP request and partially consumed request path
  • Reject, RejectHydrated - this has been rejected by the endpoint because a filter failed. This behaves just like Err but is separate because it should always be recovered. The unhydrated variant (constructed with Ap::reject() is useful for writing helper functions that exist outside of a specific server context)
  • Respond, RespondHydrated - this request has been matched with a resource and has a response to send. This implies no other endpoints or resources will see the original request.

States

  • Unhydrated - Ap that is just some data; is not a result that the server can act on
  • Hydrated - Ap that is just some data in the context of an incoming request; is not a result that the server can act on
  • CompleteWhenHydrated - Ap that is a result that the server can act on once it is lifted to the context of a request
  • Complete - Ap that is a result that the server can act on. This is the final state Aps should always reach

Type parameters

  • S: ApState - compile-time state that guarantees users complete an Ap by rejecting, responding, or erroring.
  • P: PlatformTypes - Decouples Ap from the platform being run on (e.g. heap allocation)
  • T - The type in the “Ok” channel
  • E - The type in the “Err” channel

Constructing an Ap

Destructuring an Ap

Modifying & combining Aps

Changing the value (or type) in the Ok channel

This can be done with Ap::map.

use toad::server::ap::*;
use toad::std::{dtls, PlatformTypes as Std};

let my_ap: Ap<_, Std<dtls::Y>, String, ()> = Ap::ok(1234u32).map(|n| n.to_string());
assert_eq!(my_ap, Ap::ok("1234".into()));

Changing the value (or type) in the Err channel

This can be done with Ap::map_err.

use toad::server::ap::*;
use toad::std::{dtls, PlatformTypes as Std};

#[derive(Debug)]
enum Error {
  UhOh(String),
}

let my_ap: Ap<_, Std<dtls::Y>, (), String> =
  Ap::err(Error::UhOh("failed to do the thing!".to_string())).map_err(|e| format!("{e:?}"));
assert_eq!(my_ap,
           Ap::err("UhOh(\"failed to do the thing!\")".to_string()));

Combining multiple Aps

Ap::bind is used to combine Aps. In practice, bind is identical to Result::and_then.

The provided closure will be called when the Ap is in the Ok or OkHydrated channels, and ignored when the Ap is any other state.

Hydration is contagious; if the closure returns an unhydrated variant (Ap::reject, Ap::respond, Ap::ok), and self is Ap::ok_hydrated then the closure’s return value will be hydrated before continuing. (e.g. self is ok_hydrated, closure returned reject, output will be reject_hydrated)

use toad::net::Addrd;
use toad::req::Req;
use toad::server::ap::*;
use toad::std::{dtls, PlatformTypes as Std};

#[derive(Debug, PartialEq)]
enum Error {
  UhOh(String),
}

let addr: no_std_net::SocketAddr = "1.1.1.1:5683".parse().unwrap();
let req = || Req::get("hello");

// OkHydrated.bind(Ok) => OkHydrated
let ok_hy_123: Ap<_, Std<dtls::Y>, u32, Error> =
  Ap::ok_hydrated(123, Hydrate::from_request(Addrd(req(), addr)));

let ok_hy_234 = ok_hy_123.bind(|n| Ap::ok(n + 111));

assert_eq!(ok_hy_234,
           Ap::ok_hydrated(234, Hydrate::from_request(Addrd(req(), addr))));

// OkHydrated.bind(Err) => Err
let ok_hy: Ap<_, Std<dtls::Y>, u32, Error> =
  Ap::ok_hydrated(123, Hydrate::from_request(Addrd(req(), addr)));

let err: Ap<_, _, u32, Error> = ok_hy.bind(|n| Ap::err(Error::UhOh("".into())));

assert_eq!(err, Ap::err(Error::UhOh("".into())));

// RejectHydrated.bind(Err) => RejectHydrated
let reject: Ap<_, Std<dtls::Y>, u32, Error> = Ap::reject_hydrated(Addrd(req(), addr));

let reject_unchanged: Ap<_, _, u32, Error> = reject.bind(|n| Ap::err(Error::UhOh("".into())));

assert_eq!(reject_unchanged, Ap::reject_hydrated(Addrd(req(), addr)));

Implementations§

source§

impl<P, T, E> Ap<Unhydrated, P, T, E>where P: PlatformTypes, E: Debug,

source

pub fn ok(t: T) -> Self

Construct an Ap in an Ok state

source

pub fn from_result(res: Result<T, E>) -> Self

source§

impl<P, T, Error> Ap<CompleteWhenHydrated, P, T, Error>where P: PlatformTypes, Error: Debug,

source

pub fn reject() -> Self

Construct an Ap that will reject the incoming request.

A rejected Ap should always be recovered, as it signifies “this route did not match this request” and a request must always be matched with a response.

source

pub fn respond(r: Respond<P>) -> Self

Construct an Ap that will respond to the incoming request.

source§

impl<T, P, E> Ap<Hydrated, P, T, E>where P: PlatformTypes, E: Debug,

source

pub fn bind_hydrated<F, S2, B>( self, f: F ) -> Ap<<Hydrated as Combine<S2>>::Out, P, B, E>where F: FnOnce(T, &Addrd<Req<P>>) -> Ap<S2, P, B, E>, S2: ApState, Hydrated: Combine<S2>,

Use a function F (T -> Ap<B, E>) to transform the data contained in Ap and combine the result with self.

The function will only be called if this is Ap::ok or Ap::ok_hydrated.

source

pub fn ok_hydrated(t: T, hy: Hydrate<P>) -> Ap<Hydrated, P, T, E>

Ap::ok with a Hydrate request context

source

pub fn respond_hydrated(req: Addrd<Req<P>>, rep: Respond<P>) -> Self

Ap::respond with a request context

source§

impl<P, T, E> Ap<Complete, P, T, E>where P: PlatformTypes, E: Debug,

source

pub fn pretend<S>(self) -> Ap<S, P, T, E>where S: ApState,

Coerce the state type to any other (this only applies to Aps which are known to be Complete.)

source

pub fn reject_hydrated(req: Addrd<Req<P>>) -> Self

Ap::reject with a request context

source

pub fn err(e: E) -> Self

Ap in an error channel

source

pub fn pretend_hydrated(self) -> Ap<Hydrated, P, T, E>

Pretend this Ap is Hydrated. (it must be currently Complete)

source§

impl<S, P, T, E> Ap<S, P, T, E>where P: PlatformTypes, S: ApState, E: Debug,

source

pub fn is_ok(&self) -> bool

Is this Ap::ok or Ap::ok_hydrated?

source

pub fn is_rejected(&self) -> bool

source

pub fn hydrate( self, req: Addrd<Req<P>> ) -> Ap<<S as Combine<Hydrated>>::Out, P, T, E>

source

pub fn pretend_unhydrated(self) -> Ap<Unhydrated, P, T, E>

More extreme than Ap::pretend_hydrated, this will accept an Ap of any ApState and fix it as Unhydrated.

source

pub fn try_unwrap_respond(self) -> Result<Respond<P>, Self>

If this is Ap::respond or Ap::respond_hydrated, unwrap and yield the data contained in it in Result::Ok.

If not, return Err(self).

source

pub fn try_unwrap_err(self) -> Result<E, Self>

If this is Ap::err, unwrap and yield the error contained in it in Result::Ok.

If not, return Err(self).

source

pub fn try_unwrap_ok(self) -> Result<T, Self>

If this is Ap::ok or Ap::ok_hydrated, unwrap and yield the data contained in it and return Result::Ok.

If not, return Err(self).

source

pub fn try_unwrap_ok_hydrated(self) -> Result<(T, Hydrate<P>), Self>

If this is Ap::ok_hydrated, unwrap and yield the data contained in it and return Result::Ok.

If not, return Err(self).

source

pub fn pipe<F, R>(self, f: F) -> Rwhere F: FnOnce(Self) -> R,

Apply a function that accepts Self to self.

use toad::server::ap::*;
use toad::std::{dtls, PlatformTypes as Std};

fn ok_to_err<S: state::ApState>(ap: Ap<S, Std<dtls::Y>, (), ()>) -> Ap<S, Std<dtls::Y>, (), ()> {
  if ap.is_ok() {
    Ap::err(()).pretend()
  } else {
    panic!("must be ok")
  }
}

let ap = || Ap::<_, Std<dtls::Y>, (), ()>::ok(());

// with pipe:
ap().pipe(ok_to_err);

// without:
ok_to_err(ap());
source

pub fn etag(self, etag: P::MessageOptionBytes) -> Self

If this is Ap::respond or Ap::respond_hydrated, set the etag option for the response before sending.

source

pub fn map<F, B>(self, f: F) -> Ap<S, P, B, E>where F: FnOnce(T) -> B,

Use a function F (T -> B) to transform the data contained in Ap.

The function will only be called if this is Ap::ok or Ap::ok_hydrated.

source

pub fn map_err<F, B>(self, f: F) -> Ap<S, P, T, B>where F: FnOnce(E) -> B,

Use a function F (E -> B) to transform the error contained in Ap.

The function will only be called if this is Ap::err.

source

pub fn bind<F, S2, B>(self, f: F) -> Ap<<S as Combine<S2>>::Out, P, B, E>where F: FnOnce(T) -> Ap<S2, P, B, E>, S2: ApState, S: Combine<S2>,

Use a function F (T -> Ap<B, E>) to transform the data contained in Ap and combine the result with self.

The function will only be called if this is Ap::ok or Ap::ok_hydrated.

source

pub fn bind_discard<S2, F>(self, f: F) -> Selfwhere F: for<'a> FnOnce(&'a T) -> Ap<S2, P, (), E>, S2: ApState, S: Combine<S2>,

Shorthand for binding an Ap of unit Ap<_, _, (), E> and keeping the T.

fn do_stuff(t: &T) -> Ap<_, _, (), E> { ... }

ap.bind(|t| do_stuff(&t).map(|_| t))
ap.bind_discard(do_stuff)
source

pub fn reject_on_err<E2>(self) -> Ap<Unhydrated, P, T, E2>

Silently ignore errors, mapping to Ap::reject.

Trait Implementations§

source§

impl<S, P, T, E> Clone for Ap<S, P, T, E>where S: ApState, P: PlatformTypes, E: Clone, T: Clone,

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<S, P, T, E> Debug for Ap<S, P, T, E>where S: ApState, P: PlatformTypes, E: Debug, T: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<S, P, T, E> PartialEq<Ap<S, P, T, E>> for Ap<S, P, T, E>where S: ApState, P: PlatformTypes, E: PartialEq, T: PartialEq,

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<F, A, TF, T> Sequence<F, A, TF> for T

source§

fn sequence<Ap>(self) -> <Ap as HKT1>::T<<F as HKT1>::T<A>>where Self: Sized + Traversable<F, <Ap as HKT1>::T<A>, A, TF> + Foldable<F, <Ap as HKT1>::T<A>>, Ap: HKT1, <Ap as HKT1>::T<A>: Applicative<Ap, A> + ApplyOnce<Ap, A>, <Ap as HKT1>::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>, <Ap as HKT1>::T<<F as HKT1>::T<A>>: Applicative<Ap, <F as HKT1>::T<A>> + ApplyOnce<Ap, <F as HKT1>::T<A>>, F: HKT1<T<<Ap as HKT1>::T<A>> = Self>,

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere V: MultiLane<T>,

§

fn vzip(self) -> V