dynamic_provider/
query.rs

1use core::{convert::Infallible, mem};
2
3use crate::{
4    tag::{MarkerTag, Mut, Ref, TagFor, TagId, Value},
5    Lt,
6};
7
8struct AlreadyFulfilled;
9struct ShouldRecordAttempts;
10
11/// The generic arg to [`QueryGeneric`] that unsizes to [`dyn ErasedQueryState`][ErasedQueryState].
12///
13/// This is `repr(C)` so that `&mut QueryState<T, Arg, F>` is a valid `&mut QueryState<T, Arg, Empty>`.
14#[repr(C)]
15struct QueryState<T, Arg, F> {
16    value: QueryValue<T, Arg>,
17    on_provide_attempt: F,
18}
19
20/// Internal state of a query.
21#[derive(Default)]
22enum QueryValue<T, Arg> {
23    /// Invalid state so we can move `Arg` out of `&mut Self`.
24    #[default]
25    Invalid,
26    /// Fulfilled query.
27    Value(T),
28    /// Unfulfilled query with whatever argument the tag requires.
29    Arg(Arg),
30}
31
32/// Just a `repr(C)` zero-sized type to stand in place of `F` in [`TypedQuery`].
33#[repr(C)]
34struct Empty {}
35
36/// Result of successful [`Query::downcast()`] call.
37#[repr(transparent)]
38struct TypedQuery<T, Arg> {
39    inner: QueryGeneric<QueryState<T, Arg, Empty>>,
40}
41
42impl<T, Arg> TypedQuery<T, Arg> {
43    fn fulfill(&mut self, value: T) {
44        if let QueryValue::Arg { .. } = self.inner.state.value {
45            self.inner.state.value = QueryValue::Value(value);
46            self.inner.tag_id = TagId::of::<MarkerTag<AlreadyFulfilled>>();
47        }
48    }
49
50    fn fulfill_with(&mut self, f: impl FnOnce(Arg) -> T) {
51        self.try_fulfill_with::<Infallible>(|arg| Ok(f(arg)));
52    }
53
54    fn try_fulfill_with<R>(&mut self, f: impl FnOnce(Arg) -> Result<T, (Arg, R)>) -> Option<R> {
55        if !matches!(self.inner.state.value, QueryValue::Arg { .. }) {
56            return None;
57        }
58
59        let QueryValue::Arg(arg) = mem::take(&mut self.inner.state.value) else {
60            return None;
61        };
62
63        match f(arg) {
64            Ok(value) => {
65                self.inner.state.value = QueryValue::Value(value);
66                self.inner.tag_id = TagId::of::<MarkerTag<AlreadyFulfilled>>();
67                None
68            }
69            Err((arg, out)) => {
70                self.inner.state.value = QueryValue::Arg(arg);
71                Some(out)
72            }
73        }
74    }
75}
76
77/// ## Safety
78/// It's assumed an implementor is `QueryState<Tag::Value<'x>, Tag::ArgValue<'x>>` for some `Arg: TagFor<L>`
79/// and can be downcast back to the concrete type if `Tag` is known.
80pub unsafe trait ErasedQueryState<L: Lt> {
81    fn record_attempt(&mut self, tag_id: TagId);
82}
83
84unsafe impl<L, T, Arg, F> ErasedQueryState<L> for QueryState<T, Arg, F>
85where
86    L: Lt,
87    F: FnMut(TagId),
88{
89    fn record_attempt(&mut self, tag_id: TagId) {
90        (self.on_provide_attempt)(tag_id);
91    }
92}
93/// Generic type meant for unsizing to [Query].
94#[repr(C)]
95pub struct QueryGeneric<Q: ?Sized> {
96    /// Identifies the tag type with which `state` was created.
97    /// When the query has been fulfilled, this will be set to `TagId::of::<MarkerTag<AlreadyFulfilled>>()` to indicate no future
98    /// downcasts need be attempted.
99    tag_id: TagId,
100    /// Either a `QueryState` or a `dyn ErasedQueryState` holding the query's internal state
101    /// (whether fulfilled or unfulfilled).
102    state: Q,
103}
104
105impl<T, Arg, F> QueryGeneric<QueryState<T, Arg, F>> {
106    fn new<Tag, L>(arg: Arg, on_provide_attempt: F) -> Self
107    where
108        Tag: TagFor<L, Value = T, ArgValue = Arg>,
109        L: Lt,
110    {
111        Self {
112            tag_id: TagId::of::<Tag>(),
113            state: QueryState {
114                value: QueryValue::Arg(arg),
115                on_provide_attempt,
116            },
117        }
118    }
119}
120
121/// A type-erased query ready to pass to [`Provide::provide()`][fn@crate::Provide::provide].
122///
123/// Providers may use this type to supply tagged values.
124#[repr(transparent)]
125pub struct Query<'data, L: Lt = ()> {
126    q: QueryGeneric<dyn ErasedQueryState<L> + 'data>,
127}
128
129impl<'data, L: Lt> Query<'data, L> {
130    /// Creates a `Query` expecting a value marked with `Tag` and passes it to the given function.
131    ///
132    /// Returns a pair of:
133    /// 1. The value of type `R` returned by the given function.
134    /// 1. `Some(_)` if the query was fulfilled, otherwise `None`
135    pub fn new_with<Tag, R>(
136        block: impl FnOnce(&mut Query<'data, L>) -> R,
137        arg: Tag::ArgValue,
138    ) -> (R, Option<Tag::Value>)
139    where
140        Tag: TagFor<L, ArgValue: 'data, Value: 'data>,
141    {
142        let mut query = QueryGeneric::new::<Tag, L>(arg, |_| {});
143        let out = block(Query::new_mut(&mut query as _));
144
145        let value = match query.state.value {
146            QueryValue::Value(value) => Some(value),
147            _ => None,
148        };
149
150        (out, value)
151    }
152
153    /// Creates a `Query` that does not expect any value, passes it to `block`,
154    /// and calls `on_provide_attempt()` for every available [`TagId`].
155    ///
156    /// Returns the value of type `R` returned by `block`.
157    pub fn capture_tag_ids<R>(
158        block: impl FnOnce(&mut Query<'data, L>) -> R,
159        on_provide_attempt: impl FnMut(TagId) + 'data,
160    ) -> R
161where {
162        let mut query =
163            QueryGeneric::new::<MarkerTag<ShouldRecordAttempts>, L>((), on_provide_attempt);
164        block(Query::new_mut(&mut query as _))
165    }
166
167    fn new_mut<'this>(
168        data: &'this mut QueryGeneric<dyn ErasedQueryState<L> + 'data>,
169    ) -> &'this mut Self {
170        unsafe { &mut *(data as *mut _ as *mut Self) }
171    }
172
173    fn downcast<Tag: TagFor<L>>(&mut self) -> Option<&mut TypedQuery<Tag::Value, Tag::ArgValue>> {
174        let tag_id = TagId::of::<Tag>();
175
176        if self.q.tag_id == TagId::of::<MarkerTag<ShouldRecordAttempts>>() {
177            self.q.state.record_attempt(tag_id);
178            return None;
179        }
180
181        if self.q.tag_id == tag_id {
182            // SAFETY: `Tag` is the same type used to create this query, so the underlying type should be
183            // TypedQuery<Tag::Value, Tag::ArgValue>
184            let query =
185                unsafe { &mut *(self as *mut Self as *mut TypedQuery<Tag::Value, Tag::ArgValue>) };
186
187            return Some(query);
188        }
189
190        None
191    }
192
193    /// Returns `true` if the query has been fulfilled and no values will be accepted in the future.
194    pub fn is_fulfilled(&self) -> bool {
195        self.q.tag_id == TagId::of::<MarkerTag<AlreadyFulfilled>>()
196    }
197
198    /// Returns `true` if this query would accept a value tagged with `Tag`.
199    ///
200    /// **Note**: this will return `false` if a value tagged with `Tag` _was_ expected and has been
201    /// fulfilled, as it will not accept additional values.
202    pub fn expects<Tag: TagFor<L>>(&self) -> bool {
203        self.q.tag_id == TagId::of::<Tag>()
204    }
205
206    /// Returns the [`TagId`] expected by this query.
207    ///
208    /// If this query has already been fulfilled, the returned ID is unspecified.
209    pub fn expected_tag_id(&self) -> TagId {
210        self.q.tag_id
211    }
212
213    /// Attempts to fulfill the query with a value marked with `Tag`.
214    pub fn put<Tag: TagFor<L>>(&mut self, value: Tag::Value) -> &mut Self {
215        if let Some(state) = self.downcast::<Tag>() {
216            state.fulfill(value)
217        }
218
219        self
220    }
221
222    /// Attempts to fulfill the query with a function returning a value marked with `Tag`.
223    ///
224    /// The function will not be called if the query does not accept `Tag`.
225    pub fn put_with<Tag: TagFor<L>>(
226        &mut self,
227        f: impl FnOnce(Tag::ArgValue) -> Tag::Value,
228    ) -> &mut Self {
229        if let Some(state) = self.downcast::<Tag>() {
230            state.fulfill_with(f);
231        }
232
233        self
234    }
235
236    /// Behaves similarly to [`Self::put_with()`], except that the query will **not** be fulfilled
237    /// if `predicate` returns `false`.
238    ///
239    /// The function will not be called if the query does not accept `Tag`.
240    pub fn put_where<Tag: TagFor<L>>(
241        &mut self,
242        predicate: impl FnOnce(&mut Tag::ArgValue) -> bool,
243        f: impl FnOnce(Tag::ArgValue) -> Tag::Value,
244    ) -> &mut Self {
245        self.try_put::<Tag>(|mut arg| {
246            if predicate(&mut arg) {
247                Ok(f(arg))
248            } else {
249                Err(arg)
250            }
251        })
252    }
253
254    /// Behaves similary to [`Self::put_with()`] when the function returns `Ok(_)`.
255    /// When the function returns `Err(arg)`, the query will **not** be fulfilled.
256    pub fn try_put<Tag: TagFor<L>>(
257        &mut self,
258        f: impl FnOnce(Tag::ArgValue) -> Result<Tag::Value, Tag::ArgValue>,
259    ) -> &mut Self {
260        if let Some(state) = self.downcast::<Tag>() {
261            state.try_fulfill_with(|arg| f(arg).map_err(|e| (e, ())));
262        }
263
264        self
265    }
266
267    /// Attempts to fulfill the query with a `T` marked with [`Value<T>`].
268    pub fn put_value<T: 'static>(&mut self, value: T) -> &mut Self {
269        self.put::<Value<T>>(value)
270    }
271
272    /// Attempts to fulfill the query with a function returning a `T` marked with [`Value<T>`].
273    pub fn put_value_with<T: 'static>(&mut self, value: impl FnOnce() -> T) -> &mut Self {
274        self.put::<Value<T>>(value())
275    }
276
277    /// Packs a context value of type `C` along with this query.
278    ///
279    /// The context will be consumed only when fulfilling the query.
280    /// If the query is not fulfilled, the context will be returned by [`QueryUsing::finish()`]
281    ///
282    /// ## Example
283    ///
284    /// ```
285    /// use dynamic_provider::{Lt, Provide, Query};
286    ///
287    /// #[derive(Debug)]
288    /// struct MyProvider {
289    ///     x: i32,
290    ///     y: String,
291    ///     z: Vec<u8>,
292    /// }
293    ///
294    /// impl<'x> Provide<Lt!['x]> for MyProvider {
295    ///     fn provide(self, query: &mut Query<'_, Lt!['x]>) -> Option<Self> {
296    ///         query
297    ///             .using(self)
298    ///             .put_value(|this| this.x)
299    ///             .put_value(|this| this.y)
300    ///             .put_value(|this| this.z)
301    ///             .finish()
302    ///     }
303    /// }
304    ///
305    /// let my_provider = MyProvider {
306    ///     x: 0,
307    ///     y: "Foo".into(),
308    ///     z: vec![1, 2, 3],
309    /// };
310    ///
311    /// assert_eq!(my_provider.request_value::<String>().unwrap(), "Foo");
312    /// ```
313    pub fn using<C>(&mut self, context: C) -> QueryUsing<C, L> {
314        // TODO: figure out why I need to wrap this in a function to avoid "lifetime may not live
315        // long enough"
316        fn inner<'q, L: Lt, C>(
317            this: &'q mut QueryGeneric<dyn ErasedQueryState<L> + '_>,
318            context: C,
319        ) -> QueryUsing<'q, C, L> {
320            QueryUsing {
321                context: Some(context),
322                query: Query::new_mut(this as _),
323            }
324        }
325
326        inner(&mut self.q, context)
327    }
328}
329
330impl<'x, L: Lt> Query<'_, Lt!['x, ..L]> {
331    /// Attempts to fulfill the query with a `&'x T` marked with [`Ref<Value<T>>`].
332    pub fn put_ref<T: ?Sized + 'static>(&mut self, value: &'x T) -> &mut Self {
333        self.put::<Ref<Value<T>>>(value)
334    }
335
336    /// Attempts to fulfill the query with a `&'x mut T` marked with [`Mut<Value<T>>`].
337    pub fn put_mut<T: ?Sized + 'static>(&mut self, value: &'x mut T) -> &mut Self {
338        self.put::<Mut<Value<T>>>(value)
339    }
340}
341
342/// Packs a context value of type `C` alongside the query that will be passed to a function
343/// fulfilling the query.
344///
345/// See [`Query::using()`].
346#[must_use]
347pub struct QueryUsing<'q, C, L: Lt> {
348    query: &'q mut Query<'q, L>,
349    context: Option<C>,
350}
351
352impl<C, L: Lt> QueryUsing<'_, C, L> {
353    /// Releases the context value, if still available.
354    ///
355    /// Returns `Some(_)` if the query was not fulfilled, so the context was never used.
356    /// Returns `None` if the query was fulfilled.
357    pub fn finish(self) -> Option<C> {
358        self.context
359    }
360
361    /// Returns `true` if the query has been fulfilled and no values will be accepted in the future.
362    pub fn is_fulfilled(&self) -> bool {
363        self.query.is_fulfilled()
364    }
365
366    /// Returns `true` if this query would accept a value tagged with `Tag`.
367    ///
368    /// **Note**: this will return `false` if a value tagged with `Tag` _was_ expected and has been
369    /// fulfilled, as it will not accept additional values.
370    pub fn expects<Tag: TagFor<L>>(&self) -> bool {
371        self.query.expects::<Tag>()
372    }
373
374    /// Returns the [`TagId`] expected by this query.
375    ///
376    /// If this query has already been fulfilled, the returned ID is unspecified.
377    pub fn expected_tag_id(&self) -> TagId {
378        self.query.expected_tag_id()
379    }
380
381    /// If the query is expecting `Tag`, call the given function with the [`TypedQuery`] and context.
382    /// Returns `Some(R)` value if the query expected `Tag`, otherwise `None`.
383    fn downcast_with<Tag: TagFor<L>, R>(
384        &mut self,
385        f: impl FnOnce(&mut TypedQuery<Tag::Value, Tag::ArgValue>, C) -> R,
386    ) -> Option<R> {
387        self.context.as_ref()?;
388
389        Some(f(self.query.downcast::<Tag>()?, self.context.take()?))
390    }
391
392    /// Attempts to fulfill the query using a function accepting `C` and returning a value marked by
393    /// `Tag`.
394    ///
395    /// If `Tag` is not expected, the function will not be called and the context will not be used.
396    /// Does nothing if the query has already been fulfilled.
397    pub fn put<Tag: TagFor<L>>(mut self, f: impl FnOnce(C) -> Tag::Value) -> Self {
398        self.downcast_with::<Tag, _>(|state, cx| state.fulfill(f(cx)));
399
400        self
401    }
402
403    /// Attempts to fulfill the query using a function accepting `Tag::ArgValue` and `C` and
404    /// returning a value marked by `Tag`.
405    ///
406    /// If `Tag` is not expected, the function will not be called and the context will not be used.
407    /// Does nothing if the query has already been fulfilled.
408    pub fn put_with_arg<Tag: TagFor<L>>(
409        mut self,
410        f: impl FnOnce(Tag::ArgValue, C) -> Tag::Value,
411    ) -> Self {
412        self.downcast_with::<Tag, _>(|state, cx| state.fulfill_with(|arg| f(arg, cx)));
413
414        self
415    }
416
417    /// Behaves like [`Self::put_with_arg()`], except that the query is **not** fulfilled if `predicate` returns `false`.
418    ///
419    /// If `Tag` is not expected or `predicate` returns false,
420    /// the function will not be called and the context will not be used.
421    /// Does nothing if the query has already been fulfilled.
422    pub fn put_where<Tag: TagFor<L>>(
423        self,
424        predicate: impl FnOnce(&mut Tag::ArgValue, &mut C) -> bool,
425        f: impl FnOnce(Tag::ArgValue, C) -> Tag::Value,
426    ) -> Self {
427        self.try_put_with_arg::<Tag>(|mut arg, mut cx| {
428            if predicate(&mut arg, &mut cx) {
429                Ok(f(arg, cx))
430            } else {
431                Err((arg, cx))
432            }
433        })
434    }
435
436    /// Behaves like [`Self::put()`] when the given function returns `Ok(_)`.
437    /// When the function returns `Err(context)`, the query is **not** fulfilled and the context will be usable again.
438    pub fn try_put<Tag: TagFor<L>>(self, f: impl FnOnce(C) -> Result<Tag::Value, C>) -> Self {
439        self.try_put_with_arg::<Tag>(|arg, cx| f(cx).map_err(|cx| (arg, cx)))
440    }
441
442    /// Behaves like [`Self::put_with_arg()`] when the given function returns `Ok(_)`.
443    /// When the function returns `Err((arg, context))`, the query is **not** fulfilled and the context will be usable again.
444    pub fn try_put_with_arg<Tag: TagFor<L>>(
445        mut self,
446        f: impl FnOnce(Tag::ArgValue, C) -> Result<Tag::Value, (Tag::ArgValue, C)>,
447    ) -> Self {
448        self.context = self
449            .downcast_with::<Tag, _>(|state, cx| state.try_fulfill_with(|arg| f(arg, cx)))
450            .flatten();
451
452        self
453    }
454
455    /// Attempts to fulfill the query using a function accepting `C` and returning a `T` marked by
456    /// [`Value<T>`].
457    ///
458    /// If the value is not expected, the function will not be called and the context will not be used.
459    /// Does nothing if the query has already been fulfilled.
460    pub fn put_value<T: 'static>(self, f: impl FnOnce(C) -> T) -> Self {
461        self.put::<Value<T>>(f)
462    }
463
464    /// Temporarily add another value to the context for the duration of the call to `block`.
465    /// After `block` is finished the new context will be dropped if it hasn't been used.
466    fn add_and_drop_context<C2>(
467        mut self,
468        new_context: C2,
469        block: impl for<'q2> FnOnce(QueryUsing<'q2, (C, C2), L>) -> QueryUsing<'q2, (C, C2), L>,
470    ) -> Self {
471        self.context = self
472            .context
473            .and_then(|cx| block(self.query.using((cx, new_context))).finish())
474            .map(|(cx, _)| cx);
475        self
476    }
477}
478
479impl<'x, C, L: Lt> QueryUsing<'_, C, Lt!['x, ..L]> {
480    /// Attempts to fulfill the query using a function accepting `C` and returning a `&'x T` marked by
481    /// [`Ref<Value<T>>`].
482    ///
483    /// If the reference is not expected, the function will not be called and the context will not be used.
484    /// Does nothing if the query has already been fulfilled.
485    pub fn put_ref<T: 'static + ?Sized>(self, f: impl FnOnce(C) -> &'x T) -> Self {
486        self.put::<Ref<Value<T>>>(f)
487    }
488
489    /// Attempts to fulfill the query using a function accepting `C` and returning a `&'x mut T` marked by
490    /// [`Mut<Value<T>>`].
491    ///
492    /// If the reference is not expected, the function will not be called and the context will not be used.
493    /// Does nothing if the query has already been fulfilled.
494    pub fn put_mut<T: 'static + ?Sized>(self, f: impl FnOnce(C) -> &'x mut T) -> Self {
495        self.put::<Mut<Value<T>>>(f)
496    }
497
498    /// Attempts to fulfill the query using a function accepting `C` and returning a `&'x T`.
499    /// This will supply the `&'x T` marked by [`Ref<Value<T>>`]
500    /// as well as the `T` marked by [`Value<T>`]
501    /// using `T`'s [`Clone`] implementation.
502    ///
503    /// Behaves similarly to [`Self::put_ownable()`] but is available when the `alloc` feature is disabled.
504    ///
505    /// If neither the reference nor the owned value are expected,
506    /// the function will not be called and the context will not be used.
507    /// Does nothing if the query has already been fulfilled.
508    pub fn put_cloneable<T>(self, f: impl FnOnce(C) -> &'x T) -> Self
509    where
510        T: 'static + Clone,
511    {
512        self.add_and_drop_context(f, |q| {
513            q.put_ref(|(cx, f)| f(cx))
514                .put_value(|(cx, f)| f(cx).clone())
515        })
516    }
517
518    /// Attempts to fulfill the query using a function accepting `C` and returning a `&'x T`.
519    /// This will supply the `&'x T` marked by [`Ref<Value<T>>`]
520    /// as well as the [`T::Owned`][alloc::borrow::ToOwned::Owned] marked by [`Value<T::Owned>`][Value]
521    /// using `T's` [`ToOwned`][alloc::borrow::ToOwned] implementation.
522    ///
523    /// If neither the reference nor the owned value are expected,
524    /// the function will not be called and the context will not be used.
525    /// Does nothing if the query has already been fulfilled.
526    #[cfg(any(feature = "alloc", doc))]
527    pub fn put_ownable<T>(self, f: impl FnOnce(C) -> &'x T) -> Self
528    where
529        T: 'static + ?Sized + alloc::borrow::ToOwned,
530    {
531        self.add_and_drop_context(f, |q| {
532            q.put_ref(|(cx, f)| f(cx))
533                .put_value(|(cx, f)| f(cx).to_owned())
534        })
535    }
536}