|
| 1 | +//! Whole per-file SQL-test scan results kept as one kind of the shared native store. |
| 2 | +
|
| 3 | +use pyo3::exceptions::PyValueError; |
| 4 | +use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; |
| 5 | +use pyo3::{pyclass, pymethods}; |
| 6 | +use sqlbuild_cache::digest::main::content_digest::content_digest; |
| 7 | +use sqlbuild_cache::digest::types::ContentDigest; |
| 8 | +use sqlbuild_cache::store::models::NativeStore; |
| 9 | +use std::path::PathBuf; |
| 10 | +use std::sync::Mutex; |
| 11 | + |
| 12 | +use crate::bindings::_helpers::cache::native_store::{open_store, save_store}; |
| 13 | + |
| 14 | +/// The native store kind holding SQL-test scan results. |
| 15 | +const SQL_TEST_SCAN_STORE_KIND: &str = "sql-test-scans"; |
| 16 | + |
| 17 | +/// One compile's view of the stored scan results, keyed by the digest of the caller's key parts. |
| 18 | +#[pyclass(module = "sqlbuild._native", frozen)] |
| 19 | +#[derive(Debug)] |
| 20 | +pub(crate) struct SqlTestScanStore { |
| 21 | + store: Mutex<NativeStore>, |
| 22 | +} |
| 23 | + |
| 24 | +fn poisoned<T>(_: T) -> pyo3::PyErr { |
| 25 | + PyValueError::new_err("SQL test scan store is poisoned") |
| 26 | +} |
| 27 | + |
| 28 | +#[pymethods] |
| 29 | +impl SqlTestScanStore { |
| 30 | + /// Open the store at `path` for `environment`; missing, damaged or foreign files open empty. |
| 31 | + #[new] |
| 32 | + fn new(py: Python<'_>, path: PathBuf, environment: &str) -> PyResult<Self> { |
| 33 | + let store: NativeStore = open_store(py, &path, SQL_TEST_SCAN_STORE_KIND, environment)?; |
| 34 | + Ok(Self { |
| 35 | + store: Mutex::new(store), |
| 36 | + }) |
| 37 | + } |
| 38 | + |
| 39 | + /// The value stored under the digest of `key_parts`, or `None`. |
| 40 | + fn get(&self, key_parts: Vec<String>) -> PyResult<Option<Vec<u8>>> { |
| 41 | + let key: ContentDigest = content_digest(&key_parts); |
| 42 | + Ok(self |
| 43 | + .store |
| 44 | + .lock() |
| 45 | + .map_err(poisoned)? |
| 46 | + .get(&key) |
| 47 | + .map(<[u8]>::to_vec)) |
| 48 | + } |
| 49 | + |
| 50 | + /// Store `value` under the digest of `key_parts`. |
| 51 | + fn put(&self, key_parts: Vec<String>, value: Vec<u8>) -> PyResult<()> { |
| 52 | + let key: ContentDigest = content_digest(&key_parts); |
| 53 | + self.store.lock().map_err(poisoned)?.put(key, value); |
| 54 | + Ok(()) |
| 55 | + } |
| 56 | + |
| 57 | + /// Atomically save the store when this compile stored a value; returns entries written. |
| 58 | + fn save(&self, py: Python<'_>, path: PathBuf) -> PyResult<Option<usize>> { |
| 59 | + let store = self.store.lock().map_err(poisoned)?; |
| 60 | + save_store(py, &store, &path, &[]) |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { |
| 65 | + module.add_class::<SqlTestScanStore>()?; |
| 66 | + Ok(()) |
| 67 | +} |
0 commit comments