Skip to main content

corium_query/
cache.rs

1//! Parsed-query cache keyed by the query's EDN value, as in Datomic.
2
3use std::collections::BTreeMap;
4use std::sync::{Arc, Mutex};
5
6use crate::QueryError;
7use crate::ast::{Query, parse_query};
8use crate::edn::Edn;
9
10/// Caches parsed queries by their EDN form. Cloning shares the cache.
11#[derive(Clone, Debug, Default)]
12pub struct QueryCache {
13    entries: Arc<Mutex<BTreeMap<Edn, Arc<Query>>>>,
14}
15
16impl QueryCache {
17    /// Creates an empty cache.
18    #[must_use]
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Parses a query, reusing the cached parse for a previously seen form.
24    ///
25    /// # Errors
26    /// Returns [`QueryError`] when the form fails to parse; failures are not
27    /// cached.
28    pub fn parse(&self, form: &Edn) -> Result<Arc<Query>, QueryError> {
29        let mut entries = self
30            .entries
31            .lock()
32            .unwrap_or_else(std::sync::PoisonError::into_inner);
33        if let Some(hit) = entries.get(form) {
34            return Ok(Arc::clone(hit));
35        }
36        let parsed = Arc::new(parse_query(form)?);
37        entries.insert(form.clone(), Arc::clone(&parsed));
38        Ok(parsed)
39    }
40
41    /// Number of cached parses.
42    ///
43    /// # Panics
44    /// Never; the lock is recovered if poisoned.
45    #[must_use]
46    pub fn len(&self) -> usize {
47        self.entries
48            .lock()
49            .unwrap_or_else(std::sync::PoisonError::into_inner)
50            .len()
51    }
52
53    /// Whether the cache is empty.
54    #[must_use]
55    pub fn is_empty(&self) -> bool {
56        self.len() == 0
57    }
58}