use alloc::{
collections::BTreeMap,
string::{String, ToString},
};
use core::{fmt, str::FromStr};
use crate::{RouteContext, RouteError, RouteResult};
pub trait FromRoute<S>: Sized {
fn from_route(context: &RouteContext, state: &S) -> RouteResult<Self>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct State<S>(pub S);
impl<S> FromRoute<S> for State<S>
where
S: Clone,
{
fn from_route(_context: &RouteContext, state: &S) -> RouteResult<Self> {
Ok(Self(state.clone()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Param<T>(pub T);
impl<S, T> FromRoute<S> for Param<T>
where
T: FromStr,
T::Err: fmt::Display,
{
fn from_route(context: &RouteContext, _state: &S) -> RouteResult<Self> {
let value = match context.params.len() {
0 => {
return Err(RouteError::Extract {
message: "missing route parameter".to_string(),
});
}
1 => context.params.values().next().expect("length checked"),
_ => {
return Err(RouteError::Extract {
message: "Param<T> expects exactly one parameter; use Params for multiple"
.to_string(),
});
}
};
value.parse().map(Self).map_err(|err| RouteError::Extract {
message: err.to_string(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Params(pub BTreeMap<String, String>);
impl<S> FromRoute<S> for Params {
fn from_route(context: &RouteContext, _state: &S) -> RouteResult<Self> {
Ok(Self(context.params.clone()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Input<T>(pub T);
impl<S, T> FromRoute<S> for Input<T>
where
T: Clone + Send + Sync + 'static,
{
fn from_route(context: &RouteContext, _state: &S) -> RouteResult<Self> {
context
.input
.as_ref()
.and_then(|input| input.downcast_ref::<T>())
.cloned()
.map(Self)
.ok_or_else(|| RouteError::Extract {
message: alloc::format!("missing input {}", core::any::type_name::<T>()),
})
}
}
impl<S> FromRoute<S> for RouteContext {
fn from_route(context: &RouteContext, _state: &S) -> RouteResult<Self> {
Ok(context.clone())
}
}