use topcoat_core::context::CxBuilder;
use topcoat_router::{Body, Layer, LayerFuture, Next, Path, RouterBuilder};
use crate::{CookieJarCell, write_cookies};
#[derive(Debug, Clone, Copy, Default)]
pub struct CookieLayer;
impl CookieLayer {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl Layer for CookieLayer {
fn path(&self) -> &Path {
Path::new("/")
}
fn handle<'a>(&'a self, cx: &'a mut CxBuilder, body: Body, next: Next<'a>) -> LayerFuture<'a> {
Box::pin(async move {
cx.insert(CookieJarCell::new());
let mut response = next.run(cx, body).await?;
write_cookies(cx, response.headers_mut());
Ok(response)
})
}
}
pub trait RouterBuilderCookieExt {
#[must_use]
fn cookies(self) -> Self;
}
impl RouterBuilderCookieExt for RouterBuilder {
fn cookies(self) -> Self {
self.layer(CookieLayer::new())
}
}
#[cfg(test)]
mod tests {
use http::{Method, Request, header};
use topcoat_core::{context::Cx, error::Result};
use topcoat_router::{Body, Path, Response, Route, RouteFuture, Router};
use crate::{Cookies, RouterBuilderCookieExt, cookies};
struct AddCookie;
impl Route for AddCookie {
fn method(&self) -> Method {
Method::GET
}
fn path(&self) -> &Path {
Path::new("/")
}
fn handle<'cx>(&'cx self, cx: &'cx Cx, _body: Body) -> RouteFuture<'cx> {
Box::pin(async move {
cookies(cx).add(("theme", "dark"));
Ok(Response::new(Body::empty()))
})
}
}
#[tokio::test]
async fn layer_writes_pending_cookies() -> Result<()> {
let router = Router::builder().route(AddCookie).cookies().build();
let request = Request::builder()
.uri("/")
.body(Body::empty())
.expect("request should build");
let response = router.handle(request).await;
assert_eq!(
response
.headers()
.get(header::SET_COOKIE)
.and_then(|value| value.to_str().ok()),
Some("theme=dark")
);
Ok(())
}
}