spin_key_value_spin/
lib.rs

1mod store;
2
3use std::{
4    fs,
5    path::{Path, PathBuf},
6};
7
8use anyhow::Context as _;
9use serde::{Deserialize, Serialize};
10use spin_factor_key_value::runtime_config::spin::MakeKeyValueStore;
11use store::{DatabaseLocation, KeyValueSqlite};
12
13/// A key-value store that uses SQLite as the backend.
14pub struct SpinKeyValueStore {
15    /// The base path or directory for the SQLite database file.
16    base_path: Option<PathBuf>,
17}
18
19impl SpinKeyValueStore {
20    /// Create a new SpinKeyValueStore with the given base path.
21    ///
22    /// If the database directory is None, the database will always be in-memory.
23    /// If it's `Some`, the database will be stored at the combined `base_path` and
24    /// the `path` specified in the runtime configuration.
25    pub fn new(base_path: Option<PathBuf>) -> Self {
26        Self { base_path }
27    }
28}
29
30impl MakeKeyValueStore for SpinKeyValueStore {
31    const RUNTIME_CONFIG_TYPE: &'static str = "spin";
32
33    type RuntimeConfig = SpinKeyValueRuntimeConfig;
34
35    type StoreManager = KeyValueSqlite;
36
37    fn make_store(
38        &self,
39        runtime_config: Self::RuntimeConfig,
40    ) -> anyhow::Result<Self::StoreManager> {
41        let location = match (&self.base_path, &runtime_config.path) {
42            // If both the base path and the path are specified, resolve the path against the base path
43            (Some(base_path), Some(path)) => {
44                let path = resolve_relative_path(path, base_path);
45                DatabaseLocation::Path(path)
46            }
47            // If the base path is `None` but use the path without resolving relative to the base path.
48            (None, Some(path)) => DatabaseLocation::Path(path.clone()),
49            // Otherwise, use an in-memory database
50            (None | Some(_), None) => DatabaseLocation::InMemory,
51        };
52        if let DatabaseLocation::Path(path) = &location {
53            // Create the store's parent directory if necessary
54            if let Some(parent) = path.parent().filter(|p| !p.exists()) {
55                fs::create_dir_all(parent).with_context(|| {
56                    format!(
57                        "failed to create key value store's parent directory: '{}",
58                        parent.display()
59                    )
60                })?;
61            }
62        }
63        Ok(KeyValueSqlite::new(location))
64    }
65}
66
67/// The serialized runtime configuration for the SQLite key-value store.
68#[derive(Deserialize, Serialize)]
69pub struct SpinKeyValueRuntimeConfig {
70    /// The path to the SQLite database file.
71    path: Option<PathBuf>,
72}
73
74impl SpinKeyValueRuntimeConfig {
75    /// Create a new SpinKeyValueRuntimeConfig with the given parent directory
76    /// where the key-value store will live.
77    pub fn new(path: Option<PathBuf>) -> Self {
78        Self { path }
79    }
80}
81
82/// Resolve a relative path against a base dir.
83///
84/// If the path is absolute, it is returned as is. Otherwise, it is resolved against the base dir.
85fn resolve_relative_path(path: &Path, base_dir: &Path) -> PathBuf {
86    if path.is_absolute() {
87        return path.to_owned();
88    }
89    base_dir.join(path)
90}