fnroute 0.1.0

A small function router with axum-style handler extraction.
Documentation
use fnroute::{Input, Param, RouteContext, Router, State};
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};

#[derive(Clone)]
struct AppState {
    name: String,
}

#[derive(Clone)]
struct Payload {
    action: String,
}

async fn hello() -> &'static str {
    "hello"
}

async fn user(
    Param(id): Param<u64>,
    Input(payload): Input<Payload>,
    State(state): State<AppState>,
) -> String {
    format!("{} user {id} {}", state.name, payload.action)
}

fn sync_user(Param(id): Param<u64>, State(state): State<AppState>) -> String {
    format!("{} sync user {id}", state.name)
}

fn main() {
    block_on(run());
}

async fn run() {
    let app = Router::<String>::new()
        .with_state(AppState {
            name: "demo".to_string(),
        })
        .route("hello", hello)
        .route("users/:id", user)
        .route_sync("sync-users/:id", sync_user);

    let hello = app.call("hello").await.unwrap();
    let user = app
        .call_with(RouteContext::new("users/42").input(Payload {
            action: "saved".to_string(),
        }))
        .await
        .unwrap();
    let sync_user = app.call("sync-users/7").await.unwrap();

    println!("{hello}");
    println!("{user}");
    println!("{sync_user}");
}

fn block_on<F>(future: F) -> F::Output
where
    F: Future,
{
    let waker = noop_waker();
    let mut context = Context::from_waker(&waker);
    let mut future = Box::pin(future);

    loop {
        match Pin::as_mut(&mut future).poll(&mut context) {
            Poll::Ready(output) => return output,
            Poll::Pending => std::thread::yield_now(),
        }
    }
}

fn noop_waker() -> Waker {
    unsafe fn clone(_: *const ()) -> RawWaker {
        noop_raw_waker()
    }

    unsafe fn wake(_: *const ()) {}
    unsafe fn wake_by_ref(_: *const ()) {}
    unsafe fn drop(_: *const ()) {}

    fn noop_raw_waker() -> RawWaker {
        RawWaker::new(
            std::ptr::null(),
            &RawWakerVTable::new(clone, wake, wake_by_ref, drop),
        )
    }

    unsafe { Waker::from_raw(noop_raw_waker()) }
}