Skip to main content

gpui_query/client/
erased.rs

1//! Type-erased bucket traits and persistence adapter.
2//!
3//! These traits allow `QueryClient` to store heterogeneous query and mutation
4//! buckets in a single `AHashMap<TypeId, Box<dyn Erased*>>` map, dispatching
5//! to concrete types only when the caller provides generic parameters.
6//!
7//! # Feature gating
8//!
9//! The persistence-only surface (`collect_key_status_into`, the
10//! value-carrying `collect_persistable_into`, and the legacy synchronous
11//! [`QueryPersister`] trait) is gated behind the `persist` feature. The
12//! non-persistence methods (`gc`, `invalidate_matching`, `diagnostics`, …)
13//! remain ungated so the default build is unchanged.
14
15use crate::client::devtools::{MutationDiagnostic, QueryDiagnostic};
16#[cfg(feature = "persist")]
17use crate::client::persist::{PersistedEntry, SerializerRegistry};
18use crate::core::QueryKeyFilter;
19#[cfg(feature = "persist")]
20use crate::core::{MutationStatus, QueryStatus};
21
22// `current_time_ms` moved to `client/time.rs` (ungated) so the `persist`
23// feature gate on this module's persistence symbols does not drag the GC
24// clock helper behind a `cfg`. See [`crate::client::time::current_time_ms`].
25
26/// Type-erased bucket trait for storage in a homogeneous map.
27pub(crate) trait ErasedBucket {
28    fn as_any(&self) -> &dyn std::any::Any;
29    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
30    fn gc(&mut self, now_ms: u64, gc_time_ms: u64, cx: &gpui::App);
31    fn count(&self) -> usize;
32    fn invalidate_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
33    fn reset_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
34    fn remove_matching(&mut self, filter: &QueryKeyFilter);
35    fn cancel_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
36    /// Push each live entry's diagnostic into the caller-supplied `out` Vec
37    /// instead of allocating a fresh `Vec` per bucket. Callers
38    /// (`QueryClient::diagnostics`) can pre-size a single destination Vec and
39    /// let every bucket push into it, avoiding the per-bucket allocation +
40    /// `extend` a returning variant would force.
41    fn collect_diagnostics_into(&self, now_ms: u64, cx: &gpui::App, out: &mut Vec<QueryDiagnostic>);
42    /// Push each live entry's `(key, status)` pair into `out` without building
43    /// full `QueryDiagnostic`s (#9). Used by `dehydrate`, which only needs the
44    /// key and status, avoiding the per-entry allocations of
45    /// [`collect_diagnostics_into`](ErasedBucket::collect_diagnostics_into).
46    #[cfg(feature = "persist")]
47    fn collect_key_status_into(&self, cx: &gpui::App, out: &mut Vec<(String, QueryStatus)>);
48    /// Push each `Success` entry's `(key, entry)` pair into `out`, serializing
49    /// the typed data via the caller-supplied [`SerializerRegistry`]. Entries
50    /// whose `T` has no registered serializer are skipped (metadata-only
51    /// fallback, matching the legacy `dehydrate` behavior). Used by
52    /// [`persist_with`](crate::client::QueryClient::persist_with) to build a
53    /// value-carrying [`PersistSnapshot`](crate::client::PersistSnapshot).
54    #[cfg(feature = "persist")]
55    fn collect_persistable_into(
56        &self,
57        cx: &gpui::App,
58        serializers: &SerializerRegistry,
59        now_ms: u64,
60        out: &mut Vec<(crate::core::QueryKey, PersistedEntry)>,
61    );
62}
63
64/// Type-erased infinite query bucket trait.
65pub(crate) trait ErasedInfiniteBucket {
66    fn as_any(&self) -> &dyn std::any::Any;
67    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
68    fn gc(&mut self, now_ms: u64, gc_time_ms: u64, cx: &gpui::App);
69    fn count(&self) -> usize;
70    fn invalidate_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
71    fn reset_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
72    fn remove_matching(&mut self, filter: &QueryKeyFilter);
73    fn cancel_matching(&mut self, filter: &QueryKeyFilter, cx: &mut gpui::App);
74    /// Push each live entry's diagnostic into `out`. See
75    /// [`ErasedBucket::collect_diagnostics_into`].
76    fn collect_diagnostics_into(&self, now_ms: u64, cx: &gpui::App, out: &mut Vec<QueryDiagnostic>);
77    /// Push each live entry's `(key, status)` pair into `out` without building
78    /// full `QueryDiagnostic`s (#9). See
79    /// [`ErasedBucket::collect_key_status_into`].
80    #[cfg(feature = "persist")]
81    fn collect_key_status_into(&self, cx: &gpui::App, out: &mut Vec<(String, QueryStatus)>);
82    /// Value-carrying variant for persistence. See
83    /// [`ErasedBucket::collect_persistable_into`].
84    #[cfg(feature = "persist")]
85    fn collect_persistable_into(
86        &self,
87        cx: &gpui::App,
88        serializers: &SerializerRegistry,
89        now_ms: u64,
90        out: &mut Vec<(crate::core::QueryKey, PersistedEntry)>,
91    );
92}
93
94/// Type-erased mutation bucket trait.
95pub(crate) trait ErasedMutationBucket {
96    fn as_any(&self) -> &dyn std::any::Any;
97    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
98    fn gc(&mut self, now_ms: u64, gc_time_ms: u64, cx: &gpui::App);
99    fn count(&self) -> usize;
100    /// Push each live entry's `MutationDiagnostic` into `out` instead of
101    /// allocating a fresh `Vec` per bucket. See
102    /// [`ErasedBucket::collect_diagnostics_into`] for the rationale.
103    fn collect_diagnostics_into(&self, cx: &gpui::App, out: &mut Vec<MutationDiagnostic>);
104    /// Push each live entry's `(key, status)` pair into `out` without building
105    /// full `MutationDiagnostic`s (#9). `key` is `None` for keyless mutations,
106    /// mirroring [`MutationDiagnostic::key`]. Used by `dehydrate`.
107    #[cfg(feature = "persist")]
108    fn collect_key_status_into(
109        &self,
110        cx: &gpui::App,
111        out: &mut Vec<(Option<String>, MutationStatus)>,
112    );
113}
114
115/// Legacy synchronous persistence adapter trait for query cache persistence
116/// across app restarts.
117///
118/// **Note**: this is the shipped metadata-only skeleton. The richer async,
119/// value-carrying surface lives in [`crate::client::persist`] (the
120/// [`Persister`](crate::client::persist::Persister) trait +
121/// [`persist_with`](crate::client::QueryClient::persist_with)). This trait is
122/// retained for the existing `dehydrate`/`hydrate`/`persist`/`restore` methods
123/// and is feature-gated behind `persist`.
124///
125/// Implementations can store cached data in any backend (filesystem, database, etc.).
126/// Entries are serialized as JSON strings to avoid generic bounds on the persister.
127///
128/// # Example
129///
130/// ```
131/// use std::path::PathBuf;
132/// use gpui_query::client::{QueryPersister, DehydratedEntry};
133///
134/// struct FilePersister { path: PathBuf }
135///
136/// impl QueryPersister for FilePersister {
137///     fn load(&self) -> Vec<DehydratedEntry> { Vec::new() }
138///     fn save(&self, _entries: Vec<DehydratedEntry>) {}
139/// }
140/// ```
141#[cfg(feature = "persist")]
142pub trait QueryPersister: Send + Sync {
143    /// Load persisted entries from storage.
144    fn load(&self) -> Vec<crate::client::devtools::DehydratedEntry>;
145
146    /// Save entries to storage, replacing any previously stored data.
147    fn save(&self, entries: Vec<crate::client::devtools::DehydratedEntry>);
148}