tinymist_std/concepts/
query.rs

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