Skip to main content

gpui_query/core/resource/
completion.rs

1//! Query resource completion methods.
2
3use crate::core::{CachePolicy, QueryStatus, QueryTimestamp, RequestGuard, RequestId};
4
5use super::QueryResource;
6
7impl<T, E> QueryResource<T, E> {
8    /// Complete the current request with success by request id.
9    ///
10    /// Convenience method that accepts + completes in one call.
11    /// Returns `true` if the request was accepted.
12    pub fn complete_current_success(
13        &mut self,
14        request_id: RequestId,
15        data: T,
16        now_ms: u64,
17    ) -> bool {
18        let Some(guard) = self.accept_current_request(request_id) else {
19            return false;
20        };
21        self.complete_success(guard, data, now_ms);
22        true
23    }
24
25    /// Complete the current request with failure by request id.
26    pub fn complete_current_failure(
27        &mut self,
28        request_id: RequestId,
29        error: impl Into<E>,
30        now_ms: u64,
31    ) -> bool {
32        let Some(guard) = self.accept_current_request(request_id) else {
33            return false;
34        };
35        self.complete_failure(guard, error, now_ms);
36        true
37    }
38
39    /// Complete the current request with optional success by request id.
40    pub fn complete_current_optional_success(
41        &mut self,
42        request_id: RequestId,
43        data: Option<T>,
44        now_ms: u64,
45    ) -> bool {
46        let Some(guard) = self.accept_current_request(request_id) else {
47            return false;
48        };
49        self.complete_success_optional(guard, data, now_ms);
50        true
51    }
52
53    /// Complete the current request with failure but retain data by request id.
54    pub fn complete_current_failure_with_data(
55        &mut self,
56        request_id: RequestId,
57        data: T,
58        error: impl Into<E>,
59        now_ms: u64,
60    ) -> bool {
61        let Some(guard) = self.accept_current_request(request_id) else {
62            return false;
63        };
64        self.complete_failure_with_data(guard, data, error, now_ms);
65        true
66    }
67
68    /// Complete with success, consuming the guard (two-phase protocol).
69    ///
70    /// The guard is moved, preventing double-completion at the type level.
71    /// Validates that no new request was started after the guard was issued.
72    pub fn complete_success(&mut self, guard: RequestGuard, data: T, now_ms: u64) {
73        self.validate_guard(&guard);
74        self.apply_success(data, now_ms);
75    }
76
77    /// Complete with failure, consuming the guard (two-phase protocol).
78    ///
79    /// The guard is moved, preventing double-completion at the type level.
80    /// Validates that no new request was started after the guard was issued.
81    pub fn complete_failure(&mut self, guard: RequestGuard, error: impl Into<E>, now_ms: u64) {
82        self.validate_guard(&guard);
83        self.apply_failure(error, now_ms);
84    }
85
86    /// Complete with optional success, consuming the guard.
87    ///
88    /// If `data` is `None`, the status is set to [`QueryStatus::Idle`] rather than
89    /// [`QueryStatus::Success`] to maintain the invariant that Success implies data exists.
90    pub fn complete_success_optional(&mut self, guard: RequestGuard, data: Option<T>, now_ms: u64) {
91        self.validate_guard(&guard);
92        self.apply_success_optional(data, now_ms);
93    }
94
95    /// Complete with failure but retain data, consuming the guard.
96    ///
97    /// Validates that no new request was started after the guard was issued.
98    pub fn complete_failure_with_data(
99        &mut self,
100        guard: RequestGuard,
101        data: T,
102        error: impl Into<E>,
103        now_ms: u64,
104    ) {
105        self.validate_guard(&guard);
106        self.apply_failure_with_data(data, error, now_ms);
107    }
108
109    pub(crate) fn apply_success(&mut self, data: T, now_ms: u64) {
110        self.previous_data = self.data.take();
111        self.status = QueryStatus::Success;
112        self.data = Some(data);
113        self.error = None;
114        self.active_request_id = None;
115        self.last_updated_at = Some(QueryTimestamp::from(now_ms));
116    }
117
118    pub(crate) fn apply_failure(&mut self, error: impl Into<E>, now_ms: u64) {
119        self.status = QueryStatus::Failure;
120        self.error = Some(error.into());
121        self.active_request_id = None;
122        self.last_updated_at = Some(QueryTimestamp::from(now_ms));
123    }
124
125    pub(crate) fn apply_success_optional(&mut self, data: Option<T>, now_ms: u64) {
126        self.previous_data = self.data.take();
127        // When data is None, use Idle instead of Success to maintain the
128        // invariant that Success always implies data is available. Callers
129        // that check status() == Success and then unwrap data() will not panic.
130        if data.is_some() {
131            self.status = QueryStatus::Success;
132        } else {
133            self.status = QueryStatus::Idle;
134        }
135        self.data = data;
136        self.error = None;
137        self.active_request_id = None;
138        self.last_updated_at = Some(QueryTimestamp::from(now_ms));
139    }
140
141    pub(crate) fn apply_failure_with_data(&mut self, data: T, error: impl Into<E>, now_ms: u64) {
142        self.status = QueryStatus::Failure;
143        self.data = Some(data);
144        self.error = Some(error.into());
145        self.active_request_id = None;
146        self.last_updated_at = Some(QueryTimestamp::from(now_ms));
147    }
148
149    /// Validate that the guard is still valid for the current resource state.
150    ///
151    /// `accept_current_request` clears `active_request_id`, so we cannot compare
152    /// directly. However, if `active_request_id` is `Some`, it means a *new*
153    /// request was started after the guard was issued but before completion.
154    /// This indicates the caller interleaved `begin_request` between accept and
155    /// complete, which would overwrite the newer request's state with stale data.
156    fn validate_guard(&self, guard: &RequestGuard) {
157        debug_assert!(
158            self.active_request_id.is_none(),
159            "complete_* called with stale guard (id={}) but a new request is active ({:?}). \
160             The caller must not interleave begin_request between accept and complete.",
161            guard.request_id().label(),
162            self.active_request_id,
163        );
164    }
165}
166
167impl<T, E> QueryResource<T, E> {
168    /// Whether data should be evicted after observers consume it.
169    ///
170    /// Returns `true` when `CachePolicy::NoCache` is set, meaning stored data
171    /// will never be used for cache hits and should be cleared after delivery
172    /// to avoid holding it in memory indefinitely.
173    pub fn should_clear_data_on_complete(&self) -> bool {
174        matches!(self.cache_policy, CachePolicy::NoCache)
175    }
176}