atelier/shelves/backend/main.rs

128 lines
2.8 KiB
Rust

use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use axum::handler::HandlerWithoutStateExt;
use axum::response::IntoResponse;
use std::error::Error;
use tokio::net::TcpListener;
pub type AnyError = Box<dyn Error + Send + Sync + 'static>;
pub type Result<T> = core::result::Result<T, AnyError>;
pub type Dbs = atelier::rusqlite_thread_pool::PoolSender;
pub struct RequestCtx {
dbs: Dbs,
path_params: atelier::router::PathParams,
}
fn migrate(connection: &mut rusqlite::Connection) -> Result<()> {
Ok(())
}
fn init(connection: &mut rusqlite::Connection) -> rusqlite::Result<()> {
Ok(())
}
type Request = axum::extract::Request;
type Response = axum::response::Response;
type Handler = Box<
dyn Fn(
Request,
RequestCtx,
) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
+ Send
+ Sync
+ 'static,
>;
type Router = atelier::router::Router<Handler>;
fn make_handler<Fut, F>(f: F) -> Handler
where
Fut: Future<Output = Response> + Send + 'static,
F: FnMut(Request, RequestCtx) -> Fut + Clone + Send + Sync + 'static,
{
Box::new(move |req, ctx| {
let mut f = f.clone();
Box::pin(async move { f(req, ctx).await })
})
}
async fn get_hello(req: Request, ctx: RequestCtx) -> Response {
"hello".into_response()
}
async fn get_hello_name(req: Request, ctx: RequestCtx) -> Response {
format!("Hello, {}", ctx.path_params.get("name")).into_response()
}
fn make_router() -> Router {
let mut router = atelier::router::Router::new();
macro_rules! r {
($m:expr, $p:expr, $h:expr) => {
router.register($m.to_string(), $p.to_string(), make_handler($h))
};
}
r!("GET", "/hello", get_hello);
r!("GET", "/hello/:name", get_hello_name);
router
}
async fn serve(
dbs: Dbs,
router: Arc<Router>,
req: axum::extract::Request,
) -> axum::response::Response {
let method = req.method().as_str();
let uri = format!("{}", req.uri());
let (handler, path_params) = match router.get(method, &uri) {
Ok(h) => h,
Err(sc) => {
return (http::status::StatusCode::from_u16(sc).unwrap(), "")
.into_response()
},
};
handler(req, RequestCtx { dbs, path_params }).await
}
async fn go() -> Result<()> {
let mut conn = rusqlite::Connection::open("./shelves.db")?;
migrate(&mut conn)?;
let (tx, _, _) =
atelier::rusqlite_thread_pool::spawn_threadpool_supervised(
4,
"./shelves.db",
|conn| init(conn),
)?;
let addr = "127.0.0.1:8333".parse::<SocketAddr>()?;
let listener = TcpListener::bind(addr).await?;
let router = Arc::new(make_router());
let s = move |req: Request| {
let tx = tx.clone();
let router = router.clone();
serve(tx, router.clone(), req)
};
axum::serve(listener, s.into_make_service()).await?;
Ok(())
}
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(go()).unwrap();
}