use core::{convert::Infallible, mem};
use crate::{
tag::{MarkerTag, Mut, Ref, TagFor, TagId, Value},
Lt,
};
struct AlreadyFulfilled;
struct ShouldRecordAttempts;
#[repr(C)]
struct QueryState<T, Arg, F> {
value: QueryValue<T, Arg>,
on_provide_attempt: F,
}
#[derive(Default)]
enum QueryValue<T, Arg> {
#[default]
Invalid,
Value(T),
Arg(Arg),
}
#[repr(C)]
struct Empty {}
#[repr(transparent)]
struct TypedQuery<T, Arg> {
inner: QueryGeneric<QueryState<T, Arg, Empty>>,
}
impl<T, Arg> TypedQuery<T, Arg> {
fn fulfill(&mut self, value: T) {
if let QueryValue::Arg { .. } = self.inner.state.value {
self.inner.state.value = QueryValue::Value(value);
self.inner.tag_id = TagId::of::<MarkerTag<AlreadyFulfilled>>();
}
}
fn fulfill_with(&mut self, f: impl FnOnce(Arg) -> T) {
self.try_fulfill_with::<Infallible>(|arg| Ok(f(arg)));
}
fn try_fulfill_with<R>(&mut self, f: impl FnOnce(Arg) -> Result<T, (Arg, R)>) -> Option<R> {
if !matches!(self.inner.state.value, QueryValue::Arg { .. }) {
return None;
}
let QueryValue::Arg(arg) = mem::take(&mut self.inner.state.value) else {
return None;
};
match f(arg) {
Ok(value) => {
self.inner.state.value = QueryValue::Value(value);
self.inner.tag_id = TagId::of::<MarkerTag<AlreadyFulfilled>>();
None
}
Err((arg, out)) => {
self.inner.state.value = QueryValue::Arg(arg);
Some(out)
}
}
}
}
pub unsafe trait ErasedQueryState<L: Lt> {
fn record_attempt(&mut self, tag_id: TagId);
}
unsafe impl<L, T, Arg, F> ErasedQueryState<L> for QueryState<T, Arg, F>
where
L: Lt,
F: FnMut(TagId),
{
fn record_attempt(&mut self, tag_id: TagId) {
(self.on_provide_attempt)(tag_id);
}
}
#[repr(C)]
pub struct QueryGeneric<Q: ?Sized> {
tag_id: TagId,
state: Q,
}
impl<T, Arg, F> QueryGeneric<QueryState<T, Arg, F>> {
fn new<Tag, L>(arg: Arg, on_provide_attempt: F) -> Self
where
Tag: TagFor<L, Value = T, ArgValue = Arg>,
L: Lt,
{
Self {
tag_id: TagId::of::<Tag>(),
state: QueryState {
value: QueryValue::Arg(arg),
on_provide_attempt,
},
}
}
}
#[repr(transparent)]
pub struct Query<'data, L: Lt = ()> {
q: QueryGeneric<dyn ErasedQueryState<L> + 'data>,
}
impl<'data, L: Lt> Query<'data, L> {
pub fn new_with<Tag, R>(
block: impl FnOnce(&mut Query<'data, L>) -> R,
arg: Tag::ArgValue,
) -> (R, Option<Tag::Value>)
where
Tag: TagFor<L, ArgValue: 'data, Value: 'data>,
{
let mut query = QueryGeneric::new::<Tag, L>(arg, |_| {});
let out = block(Query::new_mut(&mut query as _));
let value = match query.state.value {
QueryValue::Value(value) => Some(value),
_ => None,
};
(out, value)
}
pub fn capture_tag_ids<R>(
block: impl FnOnce(&mut Query<'data, L>) -> R,
on_provide_attempt: impl FnMut(TagId) + 'data,
) -> R
where {
let mut query =
QueryGeneric::new::<MarkerTag<ShouldRecordAttempts>, L>((), on_provide_attempt);
block(Query::new_mut(&mut query as _))
}
fn new_mut<'this>(
data: &'this mut QueryGeneric<dyn ErasedQueryState<L> + 'data>,
) -> &'this mut Self {
unsafe { &mut *(data as *mut _ as *mut Self) }
}
fn downcast<Tag: TagFor<L>>(&mut self) -> Option<&mut TypedQuery<Tag::Value, Tag::ArgValue>> {
let tag_id = TagId::of::<Tag>();
if self.q.tag_id == TagId::of::<MarkerTag<ShouldRecordAttempts>>() {
self.q.state.record_attempt(tag_id);
return None;
}
if self.q.tag_id == tag_id {
let query =
unsafe { &mut *(self as *mut Self as *mut TypedQuery<Tag::Value, Tag::ArgValue>) };
return Some(query);
}
None
}
pub fn is_fulfilled(&self) -> bool {
self.q.tag_id == TagId::of::<MarkerTag<AlreadyFulfilled>>()
}
pub fn expects<Tag: TagFor<L>>(&self) -> bool {
self.q.tag_id == TagId::of::<Tag>()
}
pub fn expected_tag_id(&self) -> TagId {
self.q.tag_id
}
pub fn put<Tag: TagFor<L>>(&mut self, value: Tag::Value) -> &mut Self {
if let Some(state) = self.downcast::<Tag>() {
state.fulfill(value)
}
self
}
pub fn put_with<Tag: TagFor<L>>(
&mut self,
f: impl FnOnce(Tag::ArgValue) -> Tag::Value,
) -> &mut Self {
if let Some(state) = self.downcast::<Tag>() {
state.fulfill_with(f);
}
self
}
pub fn put_where<Tag: TagFor<L>>(
&mut self,
predicate: impl FnOnce(&mut Tag::ArgValue) -> bool,
f: impl FnOnce(Tag::ArgValue) -> Tag::Value,
) -> &mut Self {
self.try_put::<Tag>(|mut arg| {
if predicate(&mut arg) {
Ok(f(arg))
} else {
Err(arg)
}
})
}
pub fn try_put<Tag: TagFor<L>>(
&mut self,
f: impl FnOnce(Tag::ArgValue) -> Result<Tag::Value, Tag::ArgValue>,
) -> &mut Self {
if let Some(state) = self.downcast::<Tag>() {
state.try_fulfill_with(|arg| f(arg).map_err(|e| (e, ())));
}
self
}
pub fn put_value<T: 'static>(&mut self, value: T) -> &mut Self {
self.put::<Value<T>>(value)
}
pub fn put_value_with<T: 'static>(&mut self, value: impl FnOnce() -> T) -> &mut Self {
self.put::<Value<T>>(value())
}
pub fn using<C>(&mut self, context: C) -> QueryUsing<C, L> {
fn inner<'q, L: Lt, C>(
this: &'q mut QueryGeneric<dyn ErasedQueryState<L> + '_>,
context: C,
) -> QueryUsing<'q, C, L> {
QueryUsing {
context: Some(context),
query: Query::new_mut(this as _),
}
}
inner(&mut self.q, context)
}
}
impl<'x, L: Lt> Query<'_, Lt!['x, ..L]> {
pub fn put_ref<T: ?Sized + 'static>(&mut self, value: &'x T) -> &mut Self {
self.put::<Ref<Value<T>>>(value)
}
pub fn put_mut<T: ?Sized + 'static>(&mut self, value: &'x mut T) -> &mut Self {
self.put::<Mut<Value<T>>>(value)
}
}
#[must_use]
pub struct QueryUsing<'q, C, L: Lt> {
query: &'q mut Query<'q, L>,
context: Option<C>,
}
impl<C, L: Lt> QueryUsing<'_, C, L> {
pub fn finish(self) -> Option<C> {
self.context
}
pub fn is_fulfilled(&self) -> bool {
self.query.is_fulfilled()
}
pub fn expects<Tag: TagFor<L>>(&self) -> bool {
self.query.expects::<Tag>()
}
pub fn expected_tag_id(&self) -> TagId {
self.query.expected_tag_id()
}
fn downcast_with<Tag: TagFor<L>, R>(
&mut self,
f: impl FnOnce(&mut TypedQuery<Tag::Value, Tag::ArgValue>, C) -> R,
) -> Option<R> {
self.context.as_ref()?;
Some(f(self.query.downcast::<Tag>()?, self.context.take()?))
}
pub fn put<Tag: TagFor<L>>(mut self, f: impl FnOnce(C) -> Tag::Value) -> Self {
self.downcast_with::<Tag, _>(|state, cx| state.fulfill(f(cx)));
self
}
pub fn put_with_arg<Tag: TagFor<L>>(
mut self,
f: impl FnOnce(Tag::ArgValue, C) -> Tag::Value,
) -> Self {
self.downcast_with::<Tag, _>(|state, cx| state.fulfill_with(|arg| f(arg, cx)));
self
}
pub fn put_where<Tag: TagFor<L>>(
self,
predicate: impl FnOnce(&mut Tag::ArgValue, &mut C) -> bool,
f: impl FnOnce(Tag::ArgValue, C) -> Tag::Value,
) -> Self {
self.try_put_with_arg::<Tag>(|mut arg, mut cx| {
if predicate(&mut arg, &mut cx) {
Ok(f(arg, cx))
} else {
Err((arg, cx))
}
})
}
pub fn try_put<Tag: TagFor<L>>(self, f: impl FnOnce(C) -> Result<Tag::Value, C>) -> Self {
self.try_put_with_arg::<Tag>(|arg, cx| f(cx).map_err(|cx| (arg, cx)))
}
pub fn try_put_with_arg<Tag: TagFor<L>>(
mut self,
f: impl FnOnce(Tag::ArgValue, C) -> Result<Tag::Value, (Tag::ArgValue, C)>,
) -> Self {
self.context = self
.downcast_with::<Tag, _>(|state, cx| state.try_fulfill_with(|arg| f(arg, cx)))
.flatten();
self
}
pub fn put_value<T: 'static>(self, f: impl FnOnce(C) -> T) -> Self {
self.put::<Value<T>>(f)
}
fn add_and_drop_context<C2>(
mut self,
new_context: C2,
block: impl for<'q2> FnOnce(QueryUsing<'q2, (C, C2), L>) -> QueryUsing<'q2, (C, C2), L>,
) -> Self {
self.context = self
.context
.and_then(|cx| block(self.query.using((cx, new_context))).finish())
.map(|(cx, _)| cx);
self
}
}
impl<'x, C, L: Lt> QueryUsing<'_, C, Lt!['x, ..L]> {
pub fn put_ref<T: 'static + ?Sized>(self, f: impl FnOnce(C) -> &'x T) -> Self {
self.put::<Ref<Value<T>>>(f)
}
pub fn put_mut<T: 'static + ?Sized>(self, f: impl FnOnce(C) -> &'x mut T) -> Self {
self.put::<Mut<Value<T>>>(f)
}
pub fn put_cloneable<T>(self, f: impl FnOnce(C) -> &'x T) -> Self
where
T: 'static + Clone,
{
self.add_and_drop_context(f, |q| {
q.put_ref(|(cx, f)| f(cx))
.put_value(|(cx, f)| f(cx).clone())
})
}
#[cfg(any(feature = "alloc", doc))]
pub fn put_ownable<T>(self, f: impl FnOnce(C) -> &'x T) -> Self
where
T: 'static + ?Sized + alloc::borrow::ToOwned,
{
self.add_and_drop_context(f, |q| {
q.put_ref(|(cx, f)| f(cx))
.put_value(|(cx, f)| f(cx).to_owned())
})
}
}