1use paste::paste;
2use std::{
3 any::TypeId,
4 fmt::{self, Debug, Formatter},
5 future::Future,
6 hash::{DefaultHasher, Hash, Hasher},
7 pin::Pin,
8 sync::Arc,
9};
10
11use crate::QueryOptions;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct ScopeCacheKey(u64);
15
16impl ScopeCacheKey {
17 pub fn new(fetcher_type_id: TypeId, options: &QueryOptions) -> Self {
18 let mut hasher = DefaultHasher::new();
19 fetcher_type_id.hash(&mut hasher);
20 options.hash(&mut hasher);
21 Self(hasher.finish())
22 }
23}
24
25impl Hash for ScopeCacheKey {
26 fn hash<H: Hasher>(&self, state: &mut H) {
27 self.0.hash(state);
28 }
29}
30
31#[cfg(any(
32 all(debug_assertions, feature = "devtools"),
33 feature = "devtools-always"
34))]
35#[track_caller]
36fn format_title(base: &str) -> Arc<String> {
37 let loc = std::panic::Location::caller();
38 let filepath = loc.file();
39 let file = format!(
40 "{}:{}:{}",
41 filepath
43 .split(std::path::MAIN_SEPARATOR_STR)
44 .last()
45 .unwrap_or(filepath),
46 loc.line(),
47 loc.column()
48 );
49 Arc::new(format!(
50 "{}: {}",
51 file,
52 base.trim_end_matches("::{{closure}}")
53 ))
54}
55
56pub struct QueryMarkerWithKey;
60
61pub struct QueryMarkerNoKey;
65
66macro_rules! define {
67 ([$($impl_fut_generics:tt)*], [$($impl_fn_generics:tt)*], $name:ident, $sname:literal, $sthread:literal) => {
68 #[doc = $sthread]
70 #[derive(Clone)]
76 pub struct $name<K, V> {
77 query: Arc<dyn Fn(K) -> Pin<Box<dyn Future<Output = V> $($impl_fut_generics)*>> $($impl_fn_generics)*>,
78 invalidation_hierarchy_fn: Option<Arc<dyn Fn(&K) -> Vec<String> + Send + Sync>>,
79 fetcher_type_id: TypeId,
80 cache_key: ScopeCacheKey,
81 options: QueryOptions,
82 #[cfg(any(
83 all(debug_assertions, feature = "devtools"),
84 feature = "devtools-always"
85 ))]
86 title: Arc<String>,
87 }
88
89 impl<K, V> Debug for $name<K, V> {
90 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
91 f.debug_struct(stringify!($name))
92 .field("query", &"Arc<dyn Fn(K) -> Pin<Box<dyn Future<Output = V>>")
93 .field("options", &self.options)
94 .finish()
95 }
96 }
97
98 paste! {
99 impl<K, V> $name<K, V> {
100 #[doc = $sname]
102 #[track_caller]
106 pub fn new<M>(
107 query_scope: impl [<$name Trait>]<K, V, M> $($impl_fn_generics)* + 'static,
108 ) -> Self
109 where
110 K: 'static $($impl_fn_generics)*,
111 V: 'static $($impl_fn_generics)*,
112 {
113 let options = query_scope.options().unwrap_or_default();
114 let fetcher_type_id = query_scope.fetcher_type_id();
115 Self {
116 fetcher_type_id,
117 cache_key: ScopeCacheKey::new(fetcher_type_id, &options),
118 options,
119 #[cfg(any(
120 all(debug_assertions, feature = "devtools"),
121 feature = "devtools-always"
122 ))]
123 title: query_scope.title(),
124 invalidation_hierarchy_fn: None,
125 query: Arc::new(move |key| Box::pin(query_scope.query(key))),
126 }
127 }
128
129 pub fn with_options(mut self, options: QueryOptions) -> Self {
133 self.options = options;
134 self
135 }
136
137 pub fn with_invalidation_link<S, I>(
174 mut self,
175 invalidation_hierarchy_fn: impl Fn(&K) -> I + Send + Sync + 'static
176 ) -> Self
177 where
178 I: IntoIterator<Item = S> + 'static $($impl_fn_generics)*,
179 S: Into<String> + 'static $($impl_fn_generics)*,
180 {
181 self.invalidation_hierarchy_fn = Some(Arc::new(move |key| {
182 invalidation_hierarchy_fn(key).into_iter().map(|s| s.into()).collect()
183 }));
184 self
185 }
186
187 #[cfg(any(feature = "devtools", feature = "devtools-always"))]
188 #[track_caller]
190 pub fn with_title(mut self, title: impl Into<String>) -> Self {
191 #[cfg(any(
192 all(debug_assertions, feature = "devtools"),
193 feature = "devtools-always"
194 ))]
195 {
196 self.title = format_title(&title.into());
197 }
198 self
199 }
200 }
201
202 pub trait [<$name Trait>] <K, V, M>
203 where
204 K: 'static,
205 V: 'static,
206 {
207 fn options(&self) -> Option<QueryOptions> {
208 Default::default()
209 }
210
211 fn fetcher_type_id(&self) -> TypeId;
212
213 fn cache_key(&self) -> ScopeCacheKey;
214
215 fn query(&self, key: K) -> impl Future<Output = V> $($impl_fut_generics)* + 'static;
216
217 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>>;
218
219 #[cfg(any(
220 all(debug_assertions, feature = "devtools"),
221 feature = "devtools-always"
222 ))]
223 #[track_caller]
224 fn title(&self) -> Arc<String>;
225 }
226
227 impl<K, V, F, Fut> [<$name Trait>]<K, V, QueryMarkerWithKey> for F
228 where
229 K: 'static,
230 V: 'static,
231 F: Fn(K) -> Fut + 'static,
232 Fut: Future<Output = V> $($impl_fut_generics)* + 'static,
233 {
234
235 fn fetcher_type_id(&self) -> TypeId {
236 TypeId::of::<Self>()
237 }
238
239 fn cache_key(&self) -> ScopeCacheKey {
240 ScopeCacheKey::new(TypeId::of::<Self>(), &Default::default())
241 }
242
243 fn query(&self, key: K) -> impl Future<Output = V> $($impl_fut_generics)* + 'static {
244 self(key)
245 }
246
247 fn invalidation_prefix(&self, _key: &K) -> Option<Vec<String>> {
248 None
249 }
250
251 #[cfg(any(
252 all(debug_assertions, feature = "devtools"),
253 feature = "devtools-always"
254 ))]
255 #[track_caller]
256 fn title(&self) -> Arc<String> {
257 format_title(std::any::type_name::<Self>())
258 }
259 }
260
261 impl<V, F, Fut> [<$name Trait>]<(), V, QueryMarkerNoKey> for F
262 where
263 V: 'static,
264 F: Fn() -> Fut + 'static,
265 Fut: Future<Output = V> $($impl_fut_generics)* + 'static,
266 {
267 fn fetcher_type_id(&self) -> TypeId {
268 TypeId::of::<Self>()
269 }
270
271 fn cache_key(&self) -> ScopeCacheKey {
272 ScopeCacheKey::new(TypeId::of::<Self>(), &Default::default())
273 }
274
275 fn query(&self, _key: ()) -> impl Future<Output = V> $($impl_fut_generics)* + 'static {
276 self()
277 }
278
279 fn invalidation_prefix(&self, _key: &()) -> Option<Vec<String>> {
280 None
281 }
282
283 #[cfg(any(
284 all(debug_assertions, feature = "devtools"),
285 feature = "devtools-always"
286 ))]
287 #[track_caller]
288 fn title(&self) -> Arc<String> {
289 format_title(std::any::type_name::<Self>())
290 }
291 }
292
293 impl<K, V> [<$name Trait>]<K, V, QueryMarkerWithKey> for $name<K, V>
294 where
295 K: 'static,
296 V: 'static,
297 {
298 fn options(&self) -> Option<QueryOptions> {
299 Some(self.options)
300 }
301
302 fn fetcher_type_id(&self) -> TypeId {
303 self.fetcher_type_id
304 }
305
306 fn cache_key(&self) -> ScopeCacheKey {
307 self.cache_key
308 }
309
310 fn query(&self, key: K) -> impl Future<Output = V> $($impl_fut_generics)* + 'static {
311 (self.query)(key)
312 }
313
314 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>> {
315 if let Some(invalidation_hierarchy_fn) = &self.invalidation_hierarchy_fn {
316 Some(invalidation_hierarchy_fn(key))
317 } else {
318 None
319 }
320 }
321
322 #[cfg(any(
323 all(debug_assertions, feature = "devtools"),
324 feature = "devtools-always"
325 ))]
326 #[track_caller]
327 fn title(&self) -> Arc<String> {
328 self.title.clone()
329 }
330 }
331
332 impl<K, V> [<$name Trait>]<K, V, QueryMarkerWithKey> for &$name<K, V>
333 where
334 K: 'static,
335 V: 'static,
336 {
337 fn options(&self) -> Option<QueryOptions> {
338 Some(self.options)
339 }
340
341 fn fetcher_type_id(&self) -> TypeId {
342 self.fetcher_type_id
343 }
344
345 fn cache_key(&self) -> ScopeCacheKey {
346 self.cache_key
347 }
348
349 fn query(&self, key: K) -> impl Future<Output = V> $($impl_fut_generics)* + 'static {
350 (self.query)(key)
351 }
352
353 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>> {
354 if let Some(invalidation_hierarchy_fn) = &self.invalidation_hierarchy_fn {
355 Some(invalidation_hierarchy_fn(key))
356 } else {
357 None
358 }
359 }
360
361 #[cfg(any(
362 all(debug_assertions, feature = "devtools"),
363 feature = "devtools-always"
364 ))]
365 #[track_caller]
366 fn title(&self) -> Arc<String> {
367 self.title.clone()
368 }
369 }
370
371 impl<K, V, T, M> [<$name Trait>]<K, V, M> for Arc<T>
372 where
373 K: 'static,
374 V: 'static,
375 T: [<$name Trait>]<K, V, M>,
376 {
377 fn options(&self) -> Option<QueryOptions> {
378 T::options(self)
379 }
380
381 fn fetcher_type_id(&self) -> TypeId {
382 T::fetcher_type_id(self)
383 }
384
385 fn cache_key(&self) -> ScopeCacheKey {
386 T::cache_key(self)
387 }
388
389 fn query(&self, key: K) -> impl Future<Output = V> $($impl_fut_generics)* + 'static {
390 T::query(self, key)
391 }
392
393 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>> {
394 T::invalidation_prefix(self, key)
395 }
396
397 #[cfg(any(
398 all(debug_assertions, feature = "devtools"),
399 feature = "devtools-always"
400 ))]
401 #[track_caller]
402 fn title(&self) -> Arc<String> {
403 T::title(self)
404 }
405 }
406 }
407 };
408}
409
410impl<K, V> QueryScopeLocalTrait<K, V, QueryMarkerWithKey> for QueryScope<K, V>
411where
412 K: 'static,
413 V: 'static,
414{
415 fn options(&self) -> Option<QueryOptions> {
416 Some(self.options)
417 }
418
419 fn fetcher_type_id(&self) -> TypeId {
420 self.fetcher_type_id
421 }
422
423 fn cache_key(&self) -> ScopeCacheKey {
424 self.cache_key
425 }
426
427 fn query(&self, key: K) -> impl Future<Output = V> + 'static {
428 (self.query)(key)
429 }
430
431 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>> {
432 self.invalidation_hierarchy_fn
433 .as_ref()
434 .map(|invalidation_hierarchy_fn| invalidation_hierarchy_fn(key))
435 }
436
437 #[cfg(any(
438 all(debug_assertions, feature = "devtools"),
439 feature = "devtools-always"
440 ))]
441 fn title(&self) -> Arc<String> {
442 self.title.clone()
443 }
444}
445
446impl<K, V> QueryScopeLocalTrait<K, V, QueryMarkerWithKey> for &QueryScope<K, V>
447where
448 K: 'static,
449 V: 'static,
450{
451 fn options(&self) -> Option<QueryOptions> {
452 Some(self.options)
453 }
454
455 fn fetcher_type_id(&self) -> TypeId {
456 self.fetcher_type_id
457 }
458
459 fn cache_key(&self) -> ScopeCacheKey {
460 self.cache_key
461 }
462
463 fn query(&self, key: K) -> impl Future<Output = V> + 'static {
464 (self.query)(key)
465 }
466
467 fn invalidation_prefix(&self, key: &K) -> Option<Vec<String>> {
468 self.invalidation_hierarchy_fn
469 .as_ref()
470 .map(|invalidation_hierarchy_fn| invalidation_hierarchy_fn(key))
471 }
472
473 #[cfg(any(
474 all(debug_assertions, feature = "devtools"),
475 feature = "devtools-always"
476 ))]
477 fn title(&self) -> Arc<String> {
478 self.title.clone()
479 }
480}
481
482define! { [+ Send], [+ Send + Sync], QueryScope, "QueryScope", "threadsafe" }
483define! { [], [], QueryScopeLocal, "QueryScopeLocal", "non-threadsafe" }
484
485#[derive(Debug, Clone)]
486pub(crate) struct QueryScopeInfo {
487 pub options: Option<QueryOptions>,
488 pub cache_key: ScopeCacheKey,
489 #[cfg(any(
490 all(debug_assertions, feature = "devtools"),
491 feature = "devtools-always"
492 ))]
493 pub title: Arc<String>,
494}
495
496impl QueryScopeInfo {
497 #[track_caller]
498 pub fn new<K, V, M>(query_scope: &impl QueryScopeTrait<K, V, M>) -> Self
499 where
500 K: 'static,
501 V: 'static,
502 {
503 Self {
504 options: query_scope.options(),
505 cache_key: query_scope.cache_key(),
506 #[cfg(any(
507 all(debug_assertions, feature = "devtools"),
508 feature = "devtools-always"
509 ))]
510 title: query_scope.title(),
511 }
512 }
513
514 pub fn new_local<K, V, M>(query_scope: &impl QueryScopeLocalTrait<K, V, M>) -> Self
515 where
516 K: 'static,
517 V: 'static,
518 {
519 Self {
520 options: query_scope.options(),
521 cache_key: query_scope.cache_key(),
522 #[cfg(any(
523 all(debug_assertions, feature = "devtools"),
524 feature = "devtools-always"
525 ))]
526 title: query_scope.title(),
527 }
528 }
529}