use std::future::Future;
use http::Method;
use http::Request;
use http::Response;
use http::Uri;
use tackt::route;
use tackt::Param;
use tower_service::Service;
fn create_router() -> impl Service<Request<()>, Error = Error, Response = Response<String>> {
tackt::routes![home, login, user, content]
.mount("/protected", tackt::routes![protected].with(protection))
}
#[test]
fn test() {
let mut router = create_router();
let res = oneshot(router.call(request(Method::GET, "/")));
assert_eq!(res.map(Response::into_body), Ok("home".to_string()));
let res = oneshot(router.call(request(Method::GET, "/login")));
assert_eq!(res.map(Response::into_body), Ok("login".to_string()));
let res = oneshot(router.call(request(Method::POST, "/login")));
assert_eq!(res.map(Response::into_body), Ok("login".to_string()));
let res = oneshot(router.call(request(Method::GET, "/user/1")));
assert_eq!(res.map(Response::into_body), Ok("user 1".to_string()));
let res = oneshot(router.call(request(Method::GET, "/content/1/name/path/to/file")));
assert_eq!(
res.map(Response::into_body),
Ok("content 1 name path/to/file".to_string())
);
let res = oneshot(router.call(request(Method::GET, "/protected")));
assert_eq!(res.map(Response::into_body), Err(Error::Unauthorized));
let mut req = request(Method::GET, "/protected/");
req.headers_mut()
.insert("user", "someone".try_into().unwrap());
let res = oneshot(router.call(req));
assert_eq!(res.map(Response::into_body), Ok("someone".to_string()));
}
fn respond<S: Into<String>>(body: S) -> Response<String> {
Response::new(body.into())
}
fn request(method: Method, path: &'static str) -> Request<()> {
let mut req = Request::new(());
*req.method_mut() = method;
*req.uri_mut() = Uri::from_static(path);
req
}
#[route]
async fn home(_: Request<()>) -> Result<Response<String>, Error> {
Ok(respond("home"))
}
#[route(GET, POST: "login")]
async fn login(_: Request<()>) -> Result<Response<String>, Error> {
Ok(respond("login"))
}
#[route(GET: "user" / id)]
async fn user(_: Request<()>, id: i32) -> Result<Response<String>, Error> {
Ok(respond(format!("user {}", id)))
}
#[derive(Param)]
#[route(GET: "content" / id / name / path*)]
struct Content {
id: i32,
name: String,
path: String,
}
async fn content(_: Request<()>, param: Content) -> Result<Response<String>, Error> {
Ok(respond(format!(
"content {} {} {}",
param.id, param.name, param.path
)))
}
#[route]
async fn protected(req: Request<()>) -> Result<Response<String>, Error> {
let user = req.headers().get("user").unwrap().to_str().unwrap();
Ok(respond(user))
}
async fn protection(req: Request<()>) -> Result<Request<()>, Error> {
req.headers().get("user").ok_or(Error::Unauthorized)?;
Ok(req)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Error {
Routing(tackt::Error),
Unauthorized,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::Routing(err) => write!(f, "{err}"),
Error::Unauthorized => write!(f, "unauthorized"),
}
}
}
impl From<tackt::Error> for Error {
fn from(err: tackt::Error) -> Self {
Error::Routing(err)
}
}
struct Waker(std::thread::Thread);
impl Waker {
fn new() -> std::sync::Arc<Waker> {
std::sync::Arc::new(Waker(std::thread::current()))
}
}
impl std::task::Wake for Waker {
fn wake(self: std::sync::Arc<Self>) {
self.0.unpark();
}
}
fn oneshot<T>(fut: impl Future<Output = T>) -> T {
use std::task::Context;
use std::task::Poll;
let mut fut = Box::pin(fut);
let waker = Waker::new().into();
let mut cx = Context::from_waker(&waker);
match fut.as_mut().poll(&mut cx) {
Poll::Ready(out) => out,
Poll::Pending => panic!("still pending!!!"),
}
}