use core::marker::PhantomData;
pub trait Query {
fn execute(&mut self, context: &dyn core::any::Any) -> ControlFlow;
}
pub trait QueryMut {
fn execute_mut(&mut self, context: &mut dyn core::any::Any) -> ControlFlow;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow {
Continue,
Break,
}
impl ControlFlow {
#[inline]
#[must_use]
pub fn and_then(self, f: impl FnOnce() -> Self) -> Self {
match self {
Self::Continue => f(),
Self::Break => Self::Break,
}
}
}
pub struct Once<F, Context, Outcome> {
query: Option<F>,
result: Option<Outcome>,
context: PhantomData<Context>,
}
impl<F, Context, Outcome> From<Once<F, Context, Outcome>> for Result<Outcome, Error> {
#[inline]
fn from(query: Once<F, Context, Outcome>) -> Self {
query.result.ok_or(Error::ContextTypeMismatch)
}
}
impl<F, ConnectionContext, Outcome> Once<F, ConnectionContext, Outcome>
where
F: FnOnce(&ConnectionContext) -> Outcome,
ConnectionContext: 'static,
{
#[inline]
pub fn new(query: F) -> Self {
Self {
query: Some(query),
result: None,
context: PhantomData,
}
}
}
impl<F, ConnectionContext, Outcome> Once<F, ConnectionContext, Outcome>
where
F: FnOnce(&mut ConnectionContext) -> Outcome,
ConnectionContext: 'static,
{
#[inline]
pub fn new_mut(query: F) -> Self {
Self {
query: Some(query),
result: None,
context: PhantomData,
}
}
}
impl<F, Context, Outcome> Query for Once<F, Context, Outcome>
where
F: FnOnce(&Context) -> Outcome,
Context: 'static,
{
fn execute(&mut self, context: &dyn core::any::Any) -> ControlFlow {
match context.downcast_ref::<Context>() {
Some(context) => {
let query = self.query.take().expect("can only match once");
self.result = Some(query(context));
ControlFlow::Break
}
None => ControlFlow::Continue,
}
}
}
impl<F, Context, Outcome> QueryMut for Once<F, Context, Outcome>
where
F: FnOnce(&mut Context) -> Outcome,
Context: 'static,
{
fn execute_mut(&mut self, context: &mut dyn core::any::Any) -> ControlFlow {
match context.downcast_mut::<Context>() {
Some(context) => {
let query = self.query.take().expect("can only match once");
self.result = Some(query(context));
ControlFlow::Break
}
None => ControlFlow::Continue,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Error {
ConnectionLockPoisoned,
ContextTypeMismatch,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{self:?}")
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}