//! Session support for Tsukuyomi.
#![doc(html_root_url = "https://docs.rs/tsukuyomi-session/0.2.0")]
#![deny(
missing_docs,
missing_debug_implementations,
nonstandard_style,
rust_2018_idioms,
rust_2018_compatibility,
unused
)]
#![forbid(clippy::unimplemented)]
pub mod backend;
mod util;
use {
serde::{de::DeserializeOwned, ser::Serialize},
tsukuyomi::{
error::Error, //
extractor::Extractor,
future::{MaybeDone, TryFuture},
responder::Responder,
},
};
/// A trait representing the session backend.
pub trait Backend {
/// The type of session which will be crated by `ReadSession`.
type Session: RawSession;
/// The type or errors which will occur when polling `ReadSession`.
type ReadError: Into;
/// The type of `TryFuture` that will return a `Session`.
type ReadSession: TryFuture;
/// Creates a `TryFuture` to create a `Session` asynchronously.
fn read(&self) -> Self::ReadSession;
}
impl Backend for Box
where
B: Backend,
{
type Session = B::Session;
type ReadError = B::ReadError;
type ReadSession = B::ReadSession;
#[inline]
fn read(&self) -> Self::ReadSession {
(**self).read()
}
}
impl Backend for std::rc::Rc
where
B: Backend,
{
type Session = B::Session;
type ReadError = B::ReadError;
type ReadSession = B::ReadSession;
#[inline]
fn read(&self) -> Self::ReadSession {
(**self).read()
}
}
impl Backend for std::sync::Arc
where
B: Backend,
{
type Session = B::Session;
type ReadError = B::ReadError;
type ReadSession = B::ReadSession;
#[inline]
fn read(&self) -> Self::ReadSession {
(**self).read()
}
}
/// A trait that abstracts the management of session data during request handling.
pub trait RawSession {
/// The error type during writing modification to the backend.
type WriteError: Into;
/// A `TryFuture` to write the modification of session data.
type WriteSession: TryFuture;
/// Returns the value of session data with the specified key name, if exists.
fn get(&self, name: &str) -> Option<&str>;
/// Appends a value to session data with the specified key name.
fn set(&mut self, name: &str, value: String);
/// Removes the value with the specified key name from session data.
fn remove(&mut self, name: &str);
/// Mark the session data as *cleared*.
fn clear(&mut self);
/// Consumes itself and creates a `TryFuture` to write the modification of session data.
fn write(self) -> Self::WriteSession;
}
/// Create an `Extractor` which returns a `Session`.
pub fn session(
backend: B,
) -> impl Extractor<
Output = (Session,),
Error = B::ReadError,
Extract = self::impl_extractor::SessionExtract, // private
>
where
B: Backend,
{
tsukuyomi::extractor::extract(move || self::impl_extractor::SessionExtract {
read_session: backend.read(),
})
}
mod impl_extractor {
use {
super::{RawSession, Session},
tsukuyomi::{
future::{Poll, TryFuture},
input::Input,
},
};
#[allow(missing_debug_implementations)]
pub struct SessionExtract {
pub(super) read_session: Fut,
}
impl TryFuture for SessionExtract
where
Fut: TryFuture,
Fut::Ok: RawSession,
{
type Ok = (Session,);
type Error = Fut::Error;
#[inline]
fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll {
self.read_session
.poll_ready(input)
.map(|x| x.map(|raw| (Session { raw },)))
}
}
}
/// An interface of session values.
#[derive(Debug)]
pub struct Session {
raw: S,
}
impl Session
where
S: RawSession,
{
/// Retrieves a field from this session and parses it into the specified type.
pub fn get(&self, name: &str) -> tsukuyomi::error::Result