Skip to main content

synapto_interface/
storage.rs

1use async_trait::async_trait;
2
3/// A marker trait for safe DB connection pooling
4pub trait StorageProviderPool: Send + Sync + 'static {}
5
6#[derive(Default)]
7pub struct StorageRegistry {
8    map: tokio::sync::Mutex<
9        std::collections::HashMap<
10            std::any::TypeId,
11            std::sync::Arc<dyn std::any::Any + Send + Sync>,
12        >,
13    >,
14}
15
16impl StorageRegistry {
17    /// Lazily initializes a global shared resource. If the resource already exists,
18    /// it is returned immediately. This allows multiple plugins to safely share a
19    /// single connection pool without requiring manual initialization in main.rs.
20    pub async fn get_or_init<T: StorageProviderPool, F, Fut, E>(
21        &self,
22        init: F,
23    ) -> Result<std::sync::Arc<T>, E>
24    where
25        F: FnOnce() -> Fut,
26        Fut: std::future::Future<Output = Result<T, E>>,
27    {
28        let mut map = self.map.lock().await;
29        let type_id = std::any::TypeId::of::<T>();
30
31        if let Some(resource) = map.get(&type_id) {
32            // This can only fail if the TypeId of T doesn't match the Arc's inner type,
33            // which is impossible since we keyed the HashMap by TypeId::of::<T>().
34            return Ok(resource
35                .clone()
36                .downcast::<T>()
37                .unwrap_or_else(|_| unreachable!("TypeId mismatch in StorageRegistry")));
38        }
39
40        let resource = std::sync::Arc::new(init().await?);
41        map.insert(type_id, resource.clone());
42        Ok(resource)
43    }
44}
45
46pub trait StorageConfigResolver: Send + Sync + 'static {
47    fn resolve_config(
48        &self,
49        crate_name: &str,
50        storage_type_name: &str,
51    ) -> Option<serde_json::Value>;
52}
53
54/// The entry point for a generic storage adapter.
55/// It guarantees that plugins can seamlessly initialize their underlying connection
56/// using the shared StorageRegistry without requiring manual setup in main.rs.
57#[async_trait]
58pub trait StorageConnection: Send + Sync + Sized + 'static {
59    type Config: serde::de::DeserializeOwned + Send + Sync;
60
61    async fn connect(
62        config: Self::Config,
63        storage_registry: std::sync::Arc<StorageRegistry>,
64        data_dir: &std::path::Path,
65        plugin_namespace: &str,
66    ) -> Result<Self, String>;
67}
68
69use serde::{Deserialize, Serialize, de::DeserializeOwned};
70
71#[derive(Debug, Clone, Serialize, Deserialize, Default)]
72pub struct EmptyStorageConfig {}
73
74/// For data that is just a list of items with no natural key (e.g., Behavioral Insights)
75/// The database automatically handles row creation and ID generation.
76#[async_trait]
77pub trait CollectionStore: Send + Sync + 'static {
78    /// Appends a new value to the collection.
79    ///
80    /// Note: The value type `T` MUST serialize into a JSON object-like structure
81    /// (e.g. a standard struct with named fields). Tuple structs, arrays, or primitive
82    /// types are not supported and will result in errors when saving to the store.
83    async fn push<T>(&self, collection: &str, value: T) -> Result<(), String>
84    where
85        T: Serialize + Send + Sync + 'static;
86
87    /// Retrieves all values in the collection.
88    async fn get_all<T>(&self, collection: &str) -> Result<Vec<T>, String>
89    where
90        T: DeserializeOwned + Send + Sync + 'static;
91
92    /// Clears the entire collection.
93    async fn clear(&self, collection: &str) -> Result<(), String>;
94
95    /// Replaces the entire collection with a new list of values.
96    async fn replace_all<T>(&self, collection: &str, values: Vec<T>) -> Result<(), String>
97    where
98        T: Serialize + Send + Sync + 'static;
99}
100
101/// For storing and retrieving items by a unique string ID.
102#[async_trait]
103pub trait KeyValueStore: Send + Sync + 'static {
104    async fn set<T>(&self, collection: &str, key: &str, value: T) -> Result<(), String>
105    where
106        T: Serialize + Send + Sync + 'static;
107
108    async fn get<T>(&self, collection: &str, key: &str) -> Result<Option<T>, String>
109    where
110        T: DeserializeOwned + Send + Sync + 'static;
111
112    async fn delete(&self, collection: &str, key: &str) -> Result<(), String>;
113
114    async fn get_all<T>(&self, collection: &str) -> Result<Vec<T>, String>
115    where
116        T: DeserializeOwned + Send + Sync + 'static;
117}
118
119/// Trait for storing, retrieving, and deleting raw binary files.
120#[async_trait]
121pub trait FileStore: Send + Sync + 'static {
122    /// Saves raw bytes under the specified collection and unique file identifier.
123    async fn save_file(
124        &self,
125        collection: &str,
126        file_id: &str,
127        content: Vec<u8>,
128    ) -> Result<(), String>;
129
130    /// Retrieves raw bytes by its identifier.
131    async fn get_file(&self, collection: &str, file_id: &str) -> Result<Option<Vec<u8>>, String>;
132
133    /// Deletes a file.
134    async fn delete_file(&self, collection: &str, file_id: &str) -> Result<(), String>;
135}
136#[async_trait]
137pub trait VectorStore: Send + Sync + 'static {
138    async fn setup_index(&self, collection: &str, dimension: u32) -> Result<(), String>;
139
140    async fn insert_vectors<T>(&self, collection: &str, records: Vec<T>) -> Result<(), String>
141    where
142        T: Serialize + Send + Sync + 'static;
143
144    async fn search_vectors<T>(
145        &self,
146        collection: &str,
147        vector: Vec<f32>,
148        limit: u32,
149    ) -> Result<Vec<T>, String>
150    where
151        T: DeserializeOwned + Send + Sync + 'static;
152
153    async fn delete_vectors(
154        &self,
155        collection: &str,
156        filter_field: &str,
157        filter_value: &str,
158    ) -> Result<(), String>;
159}