Skip to main content

gpui_query/core/infinite_query/
accessors.rs

1//! Accessor (getter / setter) methods for [`InfiniteQueryResource`].
2
3use std::collections::VecDeque;
4use std::sync::Arc;
5
6use super::{FetchDirection, InfiniteQueryResource};
7use crate::core::{
8    CachePolicy, QueryKey, QuerySignal, QueryStatus, QueryTimestamp, RequestId, RequestPolicy,
9    RetryPolicy,
10};
11
12// ── Accessors ────────────────────────────────────────────────────────────
13
14impl<T, E> InfiniteQueryResource<T, E> {
15    /// All loaded pages, in order from first to last.
16    ///
17    /// Pages are stored internally as `Arc<T>` (audit #5) so that fetchers can
18    /// receive a cheap `Arc::clone` via [`first_page_arc`](Self::first_page_arc) /
19    /// [`last_page_arc`](Self::last_page_arc) instead of copying the page data.
20    /// Most call sites only need a `&T` view — use [`first_page`](Self::first_page)
21    /// / [`last_page`](Self::last_page), or iterate with `.iter().map(|a| a.as_ref())`.
22    ///
23    /// **Note**: When `status()` is `Failure`, previously loaded pages are still
24    /// present and valid — the failure applies only to the most recent page fetch.
25    /// Use [`is_page_data_valid`](Self::is_page_data_valid) to check whether the
26    /// current page data can be relied upon.
27    pub fn pages(&self) -> &VecDeque<Arc<T>> {
28        &self.pages
29    }
30
31    /// Number of loaded pages.
32    pub fn page_count(&self) -> usize {
33        self.pages.len()
34    }
35
36    /// The first loaded page, if any (borrowed view).
37    pub fn first_page(&self) -> Option<&T> {
38        self.pages.front().map(|a| a.as_ref())
39    }
40
41    /// The last loaded page, if any (borrowed view).
42    pub fn last_page(&self) -> Option<&T> {
43        self.pages.back().map(|a| a.as_ref())
44    }
45
46    /// Cheap `Arc::clone` of the first page, if any (audit #5).
47    ///
48    /// Hand this to a `fetch_previous_page` fetcher instead of cloning the full
49    /// page data — only the refcount is bumped.
50    pub fn first_page_arc(&self) -> Option<Arc<T>> {
51        self.pages.front().cloned()
52    }
53
54    /// Cheap `Arc::clone` of the last page, if any (audit #5).
55    ///
56    /// Hand this to a `fetch_next_page` fetcher instead of cloning the full
57    /// page data — only the refcount is bumped.
58    pub fn last_page_arc(&self) -> Option<Arc<T>> {
59        self.pages.back().cloned()
60    }
61
62    /// Whether there are more pages after the last loaded page.
63    pub fn has_next_page(&self) -> bool {
64        self.has_next_page
65    }
66
67    /// Whether there are more pages before the first loaded page.
68    pub fn has_previous_page(&self) -> bool {
69        self.has_previous_page
70    }
71
72    /// Whether a `fetch_next_page` request is in flight.
73    pub fn is_fetching_next_page(&self) -> bool {
74        matches!(
75            self.fetching_direction,
76            Some(super::lifecycle::PageDirection::Next)
77        )
78    }
79
80    /// Whether a `fetch_previous_page` request is in flight.
81    pub fn is_fetching_previous_page(&self) -> bool {
82        matches!(
83            self.fetching_direction,
84            Some(super::lifecycle::PageDirection::Previous)
85        )
86    }
87
88    /// Maximum number of pages to retain.
89    pub fn max_pages(&self) -> Option<usize> {
90        self.max_pages
91    }
92
93    /// The fetch direction mode for this query.
94    ///
95    /// **Audit 3**: Controls the default assumptions for `has_next_page` and
96    /// `has_previous_page` after construction and after `reset()`.
97    pub fn direction(&self) -> FetchDirection {
98        self.direction
99    }
100
101    /// Current status.
102    pub fn status(&self) -> QueryStatus {
103        self.status
104    }
105
106    /// Most recent error.
107    pub fn error(&self) -> Option<&E> {
108        self.error.as_ref()
109    }
110
111    /// Whether loading.
112    pub fn is_loading(&self) -> bool {
113        self.status.is_loading()
114    }
115
116    /// Cache key.
117    pub fn key(&self) -> &QueryKey {
118        &self.key
119    }
120
121    /// Active request id.
122    pub fn active_request_id(&self) -> Option<RequestId> {
123        self.active_request_id
124    }
125
126    /// Cache policy.
127    pub fn cache_policy(&self) -> CachePolicy {
128        self.cache_policy
129    }
130
131    /// Request policy.
132    pub fn request_policy(&self) -> RequestPolicy {
133        self.request_policy
134    }
135
136    /// Set the cache policy.
137    ///
138    /// This allows policy updates on existing resources when the same key is
139    /// reused with different policies (e.g., a different TTL).
140    pub fn set_cache_policy(&mut self, policy: CachePolicy) {
141        self.cache_policy = policy;
142    }
143
144    /// Set the request policy.
145    ///
146    /// This allows policy updates on existing resources when the same key is
147    /// reused with different request behavior.
148    pub fn set_request_policy(&mut self, policy: RequestPolicy) {
149        self.request_policy = policy;
150    }
151
152    /// The retry policy for page fetches.
153    pub fn retry_policy(&self) -> &RetryPolicy {
154        &self.retry_policy
155    }
156
157    /// Set the retry policy.
158    ///
159    /// Stored by `use_infinite_query` from [`InfiniteQueryOptions::retry_policy`]
160    /// so that fetch helpers can read it from the entity.
161    pub fn set_retry_policy(&mut self, policy: RetryPolicy) {
162        self.retry_policy = policy;
163    }
164
165    /// When the current request started (ms).
166    pub fn started_at_ms(&self) -> Option<u64> {
167        self.started_at.map(QueryTimestamp::as_millis)
168    }
169
170    /// When data was last updated (ms).
171    pub fn last_updated_at_ms(&self) -> Option<u64> {
172        self.last_updated_at.map(QueryTimestamp::as_millis)
173    }
174
175    /// Cache age in milliseconds (L6).
176    ///
177    /// Mirrors [`QueryResource::cache_age_ms`]: returns `None` when there is
178    /// no recorded `last_updated_at`, and also `None` on clock skew
179    /// (`now_ms` before the recorded timestamp) via `checked_sub`. Used by
180    /// `InfiniteQueryBucket::collect_diagnostics` so the infinite diagnostic
181    /// matches the regular query's `cache_age_ms` behavior (the previous
182    /// inline `saturating_sub` returned `Some(0)` on skew).
183    ///
184    /// [`QueryResource::cache_age_ms`]: crate::core::QueryResource::cache_age_ms
185    pub fn cache_age_ms(&self, now_ms: u64) -> Option<u64> {
186        QueryTimestamp::from(now_ms).elapsed_since(self.last_updated_at?)
187    }
188
189    /// Total cache hits.
190    pub fn cache_hits(&self) -> u64 {
191        self.cache_hits
192    }
193
194    /// Total cancelled requests.
195    pub fn cancelled_count(&self) -> u64 {
196        self.cancelled_count
197    }
198
199    /// Total ignored results (completed requests whose ID no longer matched).
200    ///
201    /// Incremented when `complete_page_success` or `complete_page_failure`
202    /// receives a stale request ID, i.e. the result was produced by a fetch
203    /// that was subsequently replaced by a newer one.
204    pub fn ignored_results(&self) -> u64 {
205        self.ignored_results
206    }
207
208    /// Number of retry attempts for the current page fetch.
209    pub fn retry_count(&self) -> u32 {
210        self.retry_count
211    }
212
213    /// Increment the retry counter.
214    pub fn increment_retry(&mut self) {
215        self.retry_count = self.retry_count.saturating_add(1);
216    }
217
218    /// Increment the ignored-results counter.
219    ///
220    /// Mirrors `QueryResource::mark_ignored_result` so the client layer's
221    /// bulk-cancel path can bump `ignored_results` for infinite queries the
222    /// same way it does for regular queries (M5 core half).
223    pub fn mark_ignored_result(&mut self) {
224        self.ignored_results = self.ignored_results.saturating_add(1);
225    }
226
227    /// Reset the retry counter to zero.
228    pub fn reset_retry_count(&mut self) {
229        self.retry_count = 0;
230    }
231
232    /// Whether any pages have been loaded.
233    pub fn has_data(&self) -> bool {
234        !self.pages.is_empty()
235    }
236
237    /// Whether the currently loaded page data is valid.
238    ///
239    /// Returns `true` when:
240    /// - Status is `Success` (pages are up to date), or
241    /// - Status is `LoadingWithData` or `LoadingEmpty` (pages from a previous
242    ///   successful fetch are still valid while a new page is being fetched).
243    ///
244    /// Returns `false` when:
245    /// - Status is `Idle` (no pages have been fetched yet), or
246    /// - Status is `Cancelled` (data was explicitly cleared).
247    ///
248    /// **Important**: When status is `Failure`, this returns `true` if pages were
249    /// previously loaded. A `Failure` status means the *last page fetch* failed,
250    /// but all previously loaded pages remain valid. This is distinct from
251    /// `QueryResource` where `Failure` invalidates the single data slot.
252    pub fn is_page_data_valid(&self) -> bool {
253        match self.status {
254            QueryStatus::Success | QueryStatus::LoadingWithData => true,
255            QueryStatus::Failure => !self.pages.is_empty(),
256            QueryStatus::LoadingEmpty | QueryStatus::Idle | QueryStatus::Cancelled => false,
257        }
258    }
259
260    /// Cancellation signal.
261    pub fn signal(&self) -> Option<&QuerySignal> {
262        self.signal.as_ref()
263    }
264}