use core::fmt::Write;
pub use ap::Ap;
use toad_msg::MessageOptions;
use self::ap::state::{Complete, Hydrated};
use self::ap::{ApInner, Hydrate, Respond};
use crate::net::{Addrd, Socket};
use crate::platform::{Message, Platform, PlatformTypes};
use crate::req::Req;
use crate::resp::Resp;
use crate::step::Step;
use crate::todo::String;
pub mod ap;
pub mod path;
pub mod method;
pub mod respond;
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Error<E> {
PathDecodeError(core::str::Utf8Error),
RequestInvalidType(toad_msg::Type),
Other(E),
}
#[derive(Debug)]
pub enum Run<P, E>
where P: PlatformTypes
{
Unmatched(Addrd<Req<P>>),
Matched(Addrd<Message<P>>),
Error(Error<E>),
}
impl<P, E> PartialEq for Run<P, E>
where P: PlatformTypes,
E: PartialEq
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
| (Self::Unmatched(a), Self::Unmatched(b)) => a == b,
| (Self::Matched(a), Self::Matched(b)) => a == b,
| (Self::Error(a), Self::Error(b)) => a == b,
| _ => false,
}
}
}
impl<P, E> Run<P, E>
where P: PlatformTypes,
E: core::fmt::Debug
{
pub fn handle(ap: Ap<Complete, P, (), E>) -> Self {
match ap.0 {
| ApInner::Err(e) => Self::Error(Error::Other(e)),
| ApInner::RespondHydrated(Respond { code,
payload,
etag, },
Addrd(req, addr)) => {
let mut resp = Resp::non(&req);
resp.set_code(code);
resp.set_payload(payload);
if let Some(etag) = etag {
resp.msg_mut().add_etag(etag.as_ref()).ok();
}
Self::Matched(Addrd(resp.into(), addr))
},
| ApInner::RejectHydrated(req) => Self::Unmatched(req),
| a @ ApInner::Respond { .. }
| a @ ApInner::Reject
| a @ ApInner::Phantom(_)
| a @ ApInner::Ok(_)
| a @ ApInner::OkHydrated { .. } => unreachable!("{a:?}"),
}
}
pub fn maybe<F>(self, mut f: F) -> Self
where F: FnMut(Ap<Hydrated, P, (), E>) -> Ap<Complete, P, (), E>
{
match self {
| Run::Matched(m) => Run::Matched(m),
| Run::Error(e) => Run::Error(e),
| Run::Unmatched(req) => Self::handle(f(Ap::ok_hydrated((), Hydrate::from_request(req)))),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Init<T>(pub Option<T>);
impl Init<fn()> {
pub fn none() -> Self {
Self(None)
}
}
pub trait BlockingServer<S>: Sized + Platform<S>
where S: Step<Self::Types, PollReq = Addrd<Req<Self::Types>>, PollResp = Addrd<Resp<Self::Types>>>
{
#[allow(missing_docs)]
fn run<I, R>(&self, init: Init<I>, mut handle_request: R) -> Result<(), Error<Self::Error>>
where I: FnMut(),
R: FnMut(Run<Self::Types, Self::Error>) -> Run<Self::Types, Self::Error>
{
let mut startup_msg = String::<1000>::default();
write!(
&mut startup_msg,
r#"
=====================================
_
__ ___.--'_`.
( _`.'. - 'o` )
_\.'_' _.-'
( \`. ) //\`
\_`-'`---'\\__,
\` `-\
`
toad server up and running! 🐸
listening on `{}`.
====================================="#,
self.socket().local_addr()
).ok();
self.log(log::Level::Info, startup_msg)
.map_err(Error::Other)?;
init.0.map(|mut f| f());
loop {
let req = nb::block!(self.poll_req()).map_err(Error::Other)?;
match handle_request(Run::Unmatched(req)) {
| Run::Unmatched(req) => {
let mut msg = String::<1000>::default();
write!(&mut msg,
"IGNORING Request, not handled by any routes! {:?}",
req).ok();
self.log(log::Level::Error, msg).map_err(Error::Other)?;
let mut msg = String::<1000>::default();
write!(
&mut msg,
r#"
Do you need a fallback?
server.run(|run| run.maybe(..)
.maybe(..)
.maybe(..)
.maybe(|ap| ap.bind(|_| respond::not_found(\"Not found!\"))))
)"#
).ok();
},
| Run::Matched(rep) => nb::block!(self.send_msg(rep.clone())).map_err(Error::Other)
.map(|_| ())?,
| Run::Error(e) => break Err(e),
}
}
}
}
impl<S, T> BlockingServer<S> for T
where S: Step<Self::Types, PollReq = Addrd<Req<Self::Types>>, PollResp = Addrd<Resp<Self::Types>>>,
T: Sized + Platform<S>
{
}
#[cfg(test)]
mod tests {
mod compiles {
use crate::server::{path, respond, Error, Run};
use crate::std::{dtls, PlatformTypes as Std};
#[allow(dead_code)]
fn foo() {
let _ = Run::<Std<dtls::Y>, _>::Error(Error::Other(())).maybe(|a| {
a.pipe(path::segment::check::next_equals("user"))
.pipe(path::segment::param::u32)
.bind(|(_, user_id)| respond::ok(format!("hello, user ID {}!", user_id).into()))
});
}
}
}