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}