use salvo::extract::Metadata;
use salvo::http::StatusCode;
use salvo::prelude::{Depot, Request, Response, Text};
use salvo::writing::Writer;
use salvo::{Extractible, async_trait};
use super::GuardNotConfigured;
use crate::guard::Guard;
#[async_trait]
impl Writer for GuardNotConfigured {
async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
res.render(Text::Plain(self.to_string()));
}
}
#[allow(refining_impl_trait)]
impl<'ex> Extractible<'ex> for Guard {
fn metadata() -> &'static Metadata {
static METADATA: Metadata = Metadata::new("Guard");
&METADATA
}
async fn extract(
_req: &'ex mut Request,
depot: &'ex mut Depot,
) -> Result<Self, GuardNotConfigured> {
depot
.get_typed::<Guard>()
.cloned()
.map_err(|_| GuardNotConfigured)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn guard() -> Guard {
Guard::from_master_key(&[7u8; 32], "aes-256-gcm").unwrap()
}
#[test]
fn extracts_from_depot() {
let mut depot = Depot::new();
depot.insert_typed(guard());
let mut req = Request::new();
let extracted = futures_lite_block_on(Guard::extract(&mut req, &mut depot)).unwrap();
assert_eq!(extracted.identifiers().len(), 5);
}
#[test]
fn missing_depot_entry_is_an_error() {
let mut depot = Depot::new();
let mut req = Request::new();
let err = futures_lite_block_on(Guard::extract(&mut req, &mut depot)).unwrap_err();
assert_eq!(err, GuardNotConfigured);
}
#[test]
fn handler_and_state_share_one_encrypter() {
let state = guard();
let token = state.encrypt("共享".as_bytes()).unwrap();
let mut depot = Depot::new();
depot.insert_typed(state);
let mut req = Request::new();
let extracted = futures_lite_block_on(Guard::extract(&mut req, &mut depot)).unwrap();
assert_eq!(extracted.decrypt(&token).unwrap(), "共享".as_bytes());
}
fn futures_lite_block_on<F: std::future::Future>(fut: F) -> F::Output {
use std::task::{Context, Poll, Waker};
let mut fut = std::pin::pin!(fut);
let waker = Waker::noop();
let mut cx = Context::from_waker(waker);
loop {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(out) => return out,
Poll::Pending => std::thread::yield_now(),
}
}
}
}