use core::cmp::Ordering;
use core::marker::PhantomData;
use thiserror::Error;
use crate::error::SessionError;
use crate::property::{Property, SizedProperty};
use crate::typed::{tags, Erased, Tagged};
use crate::validate::{Validate, ValidationError};
pub use crate::context::Context;
use crate::registry::Mechanism;
pub use crate::session::SessionData;
pub trait SessionCallback: Send + Sync {
fn callback(
&self,
session_data: &SessionData,
context: &Context,
request: &mut Request,
) -> Result<(), SessionError> {
let _ = (session_data, context, request);
Ok(())
}
fn enable_channel_binding(&self) -> bool {
false
}
fn prefer<'a>(&self, a: Option<&'a Mechanism>, b: &'a Mechanism) -> Ordering {
a.map_or(Ordering::Less, |old| old.priority.cmp(&b.priority))
}
fn validate(
&self,
session_data: &SessionData,
context: &Context,
validate: &mut Validate<'_>,
) -> Result<(), ValidationError> {
let _ = (session_data, context, validate);
Ok(())
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct TOKEN(PhantomData<()>);
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CallbackError {
#[error("callback could not provide a value for this query type")]
NoValue,
#[error("callback does not handle property {0}")]
NoCallback(&'static str),
#[doc(hidden)]
#[error("callback issued early return")]
EarlyReturn(TOKEN),
}
impl CallbackError {
const fn early_return() -> Self {
Self::EarlyReturn(TOKEN(PhantomData))
}
#[must_use]
pub const fn is_no_callback(&self) -> bool {
matches!(self, Self::NoCallback(_))
}
}
pub(crate) trait CallbackRequest<Answer: ?Sized> {
fn satisfy(&mut self, answer: &Answer) -> Result<(), SessionError>;
}
enum ClosureCRState<F, G> {
Open(F),
Satisfied(G),
}
#[repr(transparent)]
pub(crate) struct ClosureCR<P, F, G> {
closure: Option<ClosureCRState<F, G>>,
_marker: PhantomData<P>,
}
impl<P, F, G> ClosureCR<P, F, G>
where
P: for<'p> Property<'p>,
F: FnOnce(&<P as Property<'_>>::Value) -> Result<G, SessionError>,
{
pub const fn wrap(closure: F) -> Self {
Self {
closure: Some(ClosureCRState::Open(closure)),
_marker: PhantomData,
}
}
pub fn try_unwrap(self) -> Option<G> {
if let Some(ClosureCRState::Satisfied(val)) = self.closure {
Some(val)
} else {
None
}
}
}
impl<P, F, G> CallbackRequest<<P as Property<'_>>::Value> for ClosureCR<P, F, G>
where
P: for<'p> Property<'p>,
F: FnOnce(&<P as Property<'_>>::Value) -> Result<G, SessionError>,
{
fn satisfy(&mut self, answer: &<P as Property<'_>>::Value) -> Result<(), SessionError> {
if let Some(ClosureCRState::Open(closure)) = self.closure.take() {
let reply = closure(answer)?;
self.closure = Some(ClosureCRState::Satisfied(reply));
}
Ok(())
}
}
#[repr(transparent)]
pub(crate) struct Satisfy<T>(PhantomData<T>);
impl<'a, T: Property<'a>> tags::MaybeSizedType<'a> for Satisfy<T> {
type Reified = dyn CallbackRequest<T::Value> + 'a;
}
#[repr(transparent)]
pub(crate) struct Action<T>(PhantomData<T>);
impl<'a, T: Property<'a>> tags::Type<'a> for Action<T> {
type Reified = Option<&'a T::Value>;
}
#[repr(transparent)]
pub struct Request<'a>(dyn Erased<'a>);
impl<'a> Request<'a> {
pub(crate) fn new_satisfy<P: for<'p> Property<'p>>(
opt: &'a mut Tagged<'a, tags::RefMut<Satisfy<P>>>,
) -> &'a mut Self {
unsafe { &mut *(opt as &mut dyn Erased as *mut dyn Erased as *mut Self) }
}
pub(crate) fn new_action<'p, P: Property<'p>>(
val: &'a mut Tagged<'p, Action<P>>,
) -> &'a mut Request<'p> {
unsafe { &mut *(val as &mut dyn Erased as *mut dyn Erased as *mut Request) }
}
}
impl<'a> Request<'a> {
fn is_satisfy<P: Property<'a>>(&self) -> bool {
self.0.is::<tags::RefMut<Satisfy<P>>>()
}
fn is_action<P: Property<'a>>(&self) -> bool {
self.0.is::<Action<P>>()
}
pub fn is<P: Property<'a>>(&self) -> bool {
self.is_satisfy::<P>() || self.is_action::<P>()
}
pub fn get_action<P: Property<'a>>(&mut self) -> Option<&'a P::Value> {
if let Some(Tagged(value)) = self.0.downcast_mut::<Action<P>>() {
value.take()
} else {
None
}
}
pub fn satisfy<P: for<'p> Property<'p>>(
&mut self,
answer: &<P as Property<'_>>::Value,
) -> Result<&mut Self, SessionError> {
if let Some(Tagged(mech)) = self.0.downcast_mut::<tags::RefMut<Satisfy<P>>>() {
mech.satisfy(answer)?;
Err(CallbackError::early_return().into())
} else {
Ok(self)
}
}
pub fn satisfy_with<'p, P: SizedProperty<'p>, F>(
&mut self,
closure: F,
) -> Result<&mut Self, SessionError>
where
F: FnOnce() -> Result<P::Value, SessionError>,
{
if let Some(Tagged(mech)) = self.0.downcast_mut::<tags::RefMut<Satisfy<P>>>() {
let answer = closure()?;
mech.satisfy(&answer)?;
Err(CallbackError::early_return().into())
} else {
Ok(self)
}
}
}
#[cfg(test)]
mod test {
static_assertions::assert_obj_safe!(super::Erased, super::SessionCallback);
}