Skip to main content

lc_vector_stores/
provider.rs

1// lc-vector-stores/src/provider.rs
2//! Vector store provider
3//!
4//! Provides a choice of multiple vector store engines: in-memory, file-persistent, Qdrant, etc.
5
6use crate::{VectorStore, VectorStoreError};
7use std::sync::Arc;
8
9/// Vector store type enum
10#[derive(Debug, Clone)]
11pub enum VectorStoreType {
12    /// In-memory storage, suitable for tests and small applications
13    InMemory,
14
15    /// File-persistent storage, suitable for personal knowledge bases
16    FileBacked {
17        /// Storage file path
18        path: String,
19        /// Vector dimension
20        dimension: usize,
21    },
22
23    /// Qdrant vector database, suitable for production
24    Qdrant {
25        /// Qdrant service URL
26        url: String,
27        /// Collection name
28        collection: String,
29    },
30}
31
32/// Vector store provider
33pub struct VectorStoreProvider;
34
35impl VectorStoreProvider {
36    /// Creates a vector store instance
37    pub async fn create(
38        store_type: VectorStoreType,
39    ) -> Result<Arc<dyn VectorStore>, VectorStoreError> {
40        match store_type {
41            VectorStoreType::InMemory => {
42                use crate::InMemoryVectorStore;
43                Ok(Arc::new(InMemoryVectorStore::new()))
44            }
45            VectorStoreType::FileBacked { path, dimension } => {
46                use crate::FileVectorStore;
47                let store = FileVectorStore::new(std::path::PathBuf::from(path), dimension)
48                    .await
49                    .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
50                Ok(Arc::new(store))
51            }
52            VectorStoreType::Qdrant { url, collection } => {
53                Self::create_qdrant_store(url, collection).await
54            }
55        }
56    }
57
58    /// Creates a Qdrant vector store
59    async fn create_qdrant_store(
60        url: String,
61        collection: String,
62    ) -> Result<Arc<dyn VectorStore>, VectorStoreError> {
63        #[cfg(feature = "qdrant-integration")]
64        {
65            use crate::{QdrantConfig, QdrantVectorStore};
66            let config = QdrantConfig::new(url, collection);
67            let store = QdrantVectorStore::new(config).await?;
68            Ok(Arc::new(store))
69        }
70
71        #[cfg(not(feature = "qdrant-integration"))]
72        {
73            // Q3: error explicitly when the feature is disabled, refusing to silently fall back
74            // to in-memory storage — production code that believes it is writing to Qdrant while
75            // actually writing to memory would lose all data on process restart.
76            Err(VectorStoreError::ConnectionError(format!(
77                "Qdrant store requires the 'qdrant-integration' feature (url={url}, collection={collection}); refusing to silently fall back to InMemory, enable the feature in Cargo.toml"
78            )))
79        }
80    }
81}
82
83/// Vector store builder providing convenient creation methods
84pub struct VectorStoreBuilder {
85    store_type: VectorStoreType,
86}
87
88impl Default for VectorStoreBuilder {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl VectorStoreBuilder {
95    /// Creates the default in-memory store builder
96    pub fn new() -> Self {
97        Self {
98            store_type: VectorStoreType::InMemory,
99        }
100    }
101
102    /// Creates an in-memory store builder
103    pub fn in_memory() -> Self {
104        Self {
105            store_type: VectorStoreType::InMemory,
106        }
107    }
108
109    /// Creates a file-persistent store builder
110    pub fn file_backed(path: impl Into<String>, dimension: usize) -> Self {
111        Self {
112            store_type: VectorStoreType::FileBacked {
113                path: path.into(),
114                dimension,
115            },
116        }
117    }
118
119    /// Creates a Qdrant store builder
120    pub fn qdrant(url: impl Into<String>, collection: impl Into<String>) -> Self {
121        Self {
122            store_type: VectorStoreType::Qdrant {
123                url: url.into(),
124                collection: collection.into(),
125            },
126        }
127    }
128
129    /// Builds a vector store instance
130    pub async fn build(self) -> Result<Arc<dyn VectorStore>, VectorStoreError> {
131        VectorStoreProvider::create(self.store_type).await
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[tokio::test]
140    async fn test_create_in_memory() {
141        let result = VectorStoreProvider::create(VectorStoreType::InMemory).await;
142        assert!(result.is_ok());
143    }
144
145    #[tokio::test]
146    async fn test_builder_in_memory() {
147        let builder = VectorStoreBuilder::in_memory();
148        let store = builder.build().await;
149        assert!(store.is_ok());
150    }
151
152    #[cfg(not(feature = "qdrant-integration"))]
153    #[tokio::test]
154    async fn test_builder_qdrant_errors_when_feature_disabled() {
155        // Q3: when the feature is disabled, it must error explicitly, never silently fall back to in-memory storage.
156        let builder = VectorStoreBuilder::qdrant("http://localhost:6334", "test_collection");
157        let store = builder.build().await;
158        assert!(store.is_err());
159    }
160}