query_flow/
key.rs

1//! Key types for query caching.
2
3use std::any::TypeId;
4use std::fmt::Debug;
5use std::hash::{Hash, Hasher};
6use std::sync::Arc;
7
8use crate::asset::FullAssetKey;
9
10/// Trait for query cache keys.
11///
12/// Cache keys must be hashable, comparable, cloneable, and thread-safe.
13pub trait Key: Hash + Eq + Clone + Send + Sync + Debug + 'static {}
14
15// Blanket implementation for all types that satisfy the bounds
16impl<T> Key for T where T: Hash + Eq + Clone + Send + Sync + Debug + 'static {}
17
18/// Marker type to distinguish asset keys in the dependency graph.
19struct AssetKeyMarker;
20
21/// Marker type for query set sentinels (used by list_queries).
22struct QuerySetMarker;
23
24/// Marker type for asset key set sentinels (used by list_asset_keys).
25struct AssetKeySetMarker;
26
27/// Full cache key that includes query type information.
28///
29/// This prevents collisions between different query types that might
30/// have the same `CacheKey` type (e.g., both use `u32`).
31///
32/// This type is used internally for dependency tracking, but is also
33/// exposed for error reporting and future GC functionality.
34#[derive(Clone)]
35pub struct FullCacheKey {
36    /// Type ID of the query (or AssetKeyMarker for assets)
37    query_type: TypeId,
38    /// Hash of the user's cache key
39    key_hash: u64,
40    /// Debug representation for error messages
41    debug_repr: Arc<str>,
42}
43
44impl FullCacheKey {
45    /// Create a new full cache key for a query.
46    pub fn new<Q: 'static, K: Key>(key: &K) -> Self {
47        let mut hasher = ahash::AHasher::default();
48        key.hash(&mut hasher);
49        let key_hash = hasher.finish();
50
51        Self {
52            query_type: TypeId::of::<Q>(),
53            key_hash,
54            debug_repr: Arc::from(format!("{}({:?})", std::any::type_name::<Q>(), key)),
55        }
56    }
57
58    /// Create a cache key from an asset key.
59    ///
60    /// This allows assets to participate in the same dependency graph as queries.
61    /// Assets use a special marker type to namespace them separately from queries.
62    pub(crate) fn from_asset_key(asset_key: &FullAssetKey) -> Self {
63        // Combine asset key's type and hash to create a unique key
64        let mut hasher = ahash::AHasher::default();
65        asset_key.key_type().hash(&mut hasher);
66        asset_key.key_hash().hash(&mut hasher);
67        let key_hash = hasher.finish();
68
69        Self {
70            query_type: TypeId::of::<AssetKeyMarker>(),
71            key_hash,
72            debug_repr: Arc::from(asset_key.debug_repr()),
73        }
74    }
75
76    /// Get debug representation for error messages.
77    pub fn debug_repr(&self) -> &str {
78        &self.debug_repr
79    }
80
81    /// Create a sentinel key representing "all queries of type Q".
82    ///
83    /// This is used by `list_queries` to track dependencies on the set of queries,
84    /// rather than individual query values.
85    pub fn query_set_sentinel<Q: 'static>() -> Self {
86        let mut hasher = ahash::AHasher::default();
87        TypeId::of::<Q>().hash(&mut hasher);
88        let type_hash = hasher.finish();
89
90        Self {
91            query_type: TypeId::of::<QuerySetMarker>(),
92            key_hash: type_hash,
93            debug_repr: Arc::from(format!("QuerySet<{}>", std::any::type_name::<Q>())),
94        }
95    }
96
97    /// Create a sentinel key representing "all asset keys of type K".
98    ///
99    /// This is used by `list_asset_keys` to track dependencies on the set of asset keys,
100    /// rather than individual asset values.
101    pub fn asset_key_set_sentinel<K: 'static>() -> Self {
102        let mut hasher = ahash::AHasher::default();
103        TypeId::of::<K>().hash(&mut hasher);
104        let type_hash = hasher.finish();
105
106        Self {
107            query_type: TypeId::of::<AssetKeySetMarker>(),
108            key_hash: type_hash,
109            debug_repr: Arc::from(format!("AssetKeySet<{}>", std::any::type_name::<K>())),
110        }
111    }
112}
113
114impl Debug for FullCacheKey {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        write!(f, "{}", self.debug_repr)
117    }
118}
119
120impl Hash for FullCacheKey {
121    fn hash<H: Hasher>(&self, state: &mut H) {
122        self.query_type.hash(state);
123        self.key_hash.hash(state);
124    }
125}
126
127impl PartialEq for FullCacheKey {
128    fn eq(&self, other: &Self) -> bool {
129        self.query_type == other.query_type && self.key_hash == other.key_hash
130    }
131}
132
133impl Eq for FullCacheKey {}