tinymist_std/concepts/
query.rs

1use core::fmt;
2use std::sync::OnceLock;
3
4use parking_lot::Mutex;
5
6/// Represent the result of an immutable query reference.
7/// The compute function should be pure enough.
8///
9/// [`compute`]: Self::compute
10/// [`compute_ref`]: Self::compute_ref
11pub struct QueryRef<Res, Err, QueryContext = ()> {
12    ctx: Mutex<Option<QueryContext>>,
13    /// `None` means no value has been computed yet.
14    cell: OnceLock<Result<Res, Err>>,
15}
16
17impl<T, E, QC> QueryRef<T, E, QC> {
18    /// Create a new query reference with the given value.
19    pub fn with_value(value: T) -> Self {
20        let cell = OnceLock::new();
21        cell.get_or_init(|| Ok(value));
22        Self {
23            ctx: Mutex::new(None),
24            cell,
25        }
26    }
27
28    /// Create a new query reference with the given context to execute the
29    /// query.
30    pub fn with_context(ctx: QC) -> Self {
31        Self {
32            ctx: Mutex::new(Some(ctx)),
33            cell: OnceLock::new(),
34        }
35    }
36}
37
38impl<T, E: Clone, QC> QueryRef<T, E, QC> {
39    /// Compute and return a checked reference guard.
40    #[inline]
41    pub fn compute<F: FnOnce() -> Result<T, E>>(&self, f: F) -> Result<&T, E> {
42        self.compute_with_context(|_| f())
43    }
44
45    /// Compute with context and return a checked reference guard.
46    #[inline]
47    pub fn compute_with_context<F: FnOnce(QC) -> Result<T, E>>(&self, f: F) -> Result<&T, E> {
48        let result = self.cell.get_or_init(|| f(self.ctx.lock().take().unwrap()));
49        result.as_ref().map_err(Clone::clone)
50    }
51
52    /// Gets the reference to the (maybe uninitialized) result.
53    ///
54    /// Returns `None` if the cell is empty, or being initialized. This
55    /// method never blocks.
56    ///
57    /// It is possible not hot, so that it is non-inlined
58    pub fn get_uninitialized(&self) -> Option<&Result<T, E>> {
59        self.cell.get()
60    }
61}
62
63impl<T, E> Default for QueryRef<T, E> {
64    fn default() -> Self {
65        QueryRef {
66            ctx: Mutex::new(Some(())),
67            cell: OnceLock::new(),
68        }
69    }
70}
71
72impl<T, E, QC> fmt::Debug for QueryRef<T, E, QC>
73where
74    T: fmt::Debug,
75    E: fmt::Debug,
76    QC: fmt::Debug,
77{
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        let ctx = self.ctx.lock();
80        let res = self.cell.get();
81        f.debug_struct("QueryRef")
82            .field("context", &ctx)
83            .field("result", &res)
84            .finish()
85    }
86}