Skip to main content

scoped_heed/
builder.rs

1use crate::{
2    GlobalScopeRegistry, ScopedBytesDatabase, ScopedBytesKeyDatabase, ScopedDatabase, ScopedDbError,
3};
4use heed::{Env, RwTxn};
5use serde::{Deserialize, Serialize};
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9/// Builder for creating scoped databases with flexible type configurations
10pub struct ScopedDatabaseOptions<'env> {
11    env: &'env Env,
12    global_registry: Arc<GlobalScopeRegistry>,
13}
14
15impl<'env> ScopedDatabaseOptions<'env> {
16    /// Create a new options builder
17    pub fn new(env: &'env Env, global_registry: Arc<GlobalScopeRegistry>) -> Self {
18        Self {
19            env,
20            global_registry,
21        }
22    }
23
24    /// Alias for backward compatibility
25    pub fn with_registry(self, _registry: Arc<GlobalScopeRegistry>) -> Self {
26        // Registry is already provided at construction time
27        self
28    }
29
30    /// Configure database with generic key and value types using SerdeBincode
31    /// Keys and values are serialized using bincode
32    pub fn types<K, V>(self) -> TypedOptions<'env, K, V>
33    where
34        K: Serialize + for<'de> Deserialize<'de> + Clone + 'static,
35        V: Serialize + for<'de> Deserialize<'de> + 'static,
36    {
37        TypedOptions {
38            env: self.env,
39            name: None,
40            global_registry: self.global_registry,
41            _phantom: PhantomData,
42        }
43    }
44
45    /// Configure database with raw byte slice keys (&[u8]) and serialized values
46    /// Keys are stored as-is without serialization, values use bincode
47    pub fn bytes_keys<V>(self) -> BytesKeysOptions<'env, V>
48    where
49        V: Serialize + for<'de> Deserialize<'de> + 'static,
50    {
51        BytesKeysOptions {
52            env: self.env,
53            name: None,
54            global_registry: self.global_registry,
55            _phantom: PhantomData,
56        }
57    }
58
59    /// Configure database with raw byte slice keys and values (no serialization)
60    /// Both keys and values are stored as raw bytes without any encoding
61    pub fn raw_bytes(self) -> RawBytesOptions<'env> {
62        RawBytesOptions {
63            env: self.env,
64            name: None,
65            global_registry: self.global_registry,
66            use_unnamed_for_default: false,
67        }
68    }
69}
70
71/// Options for generic typed databases (serialized keys and values)
72pub struct TypedOptions<'env, K, V> {
73    env: &'env Env,
74    name: Option<String>,
75    global_registry: Arc<GlobalScopeRegistry>,
76    _phantom: PhantomData<(K, V)>,
77}
78
79impl<K, V> TypedOptions<'_, K, V>
80where
81    K: Serialize + for<'de> Deserialize<'de> + Clone + Default + 'static,
82    V: Serialize + for<'de> Deserialize<'de> + 'static,
83{
84    /// Set the database name
85    pub fn name(mut self, name: &str) -> Self {
86        self.name = Some(name.to_string());
87        self
88    }
89
90    /// Create the database with the current transaction
91    pub fn create(self, txn: &mut RwTxn) -> Result<ScopedDatabase<K, V>, ScopedDbError> {
92        let name = self
93            .name
94            .ok_or_else(|| ScopedDbError::InvalidInput("Database name is required".into()))?;
95
96        // Always use the global registry (required for scope management)
97        ScopedDatabase::create(self.env, &name, txn, self.global_registry.clone())
98    }
99}
100
101/// Options for databases with byte keys and serialized values
102pub struct BytesKeysOptions<'env, V> {
103    env: &'env Env,
104    name: Option<String>,
105    global_registry: Arc<GlobalScopeRegistry>,
106    _phantom: PhantomData<V>,
107}
108
109impl<V> BytesKeysOptions<'_, V>
110where
111    V: Serialize + for<'de> Deserialize<'de> + 'static,
112{
113    /// Set the database name
114    pub fn name(mut self, name: &str) -> Self {
115        self.name = Some(name.to_string());
116        self
117    }
118
119    /// Create the database with the current transaction
120    pub fn create(self, txn: &mut RwTxn) -> Result<ScopedBytesKeyDatabase<V>, ScopedDbError> {
121        let name = self
122            .name
123            .ok_or_else(|| ScopedDbError::InvalidInput("Database name is required".into()))?;
124
125        crate::scoped_bytes_key_database::ScopedBytesKeyDatabase::create(
126            self.env,
127            &name,
128            txn,
129            self.global_registry.clone(),
130        )
131    }
132}
133
134/// Options for pure raw bytes databases (no serialization)
135pub struct RawBytesOptions<'env> {
136    env: &'env Env,
137    name: Option<String>,
138    global_registry: Arc<GlobalScopeRegistry>,
139    use_unnamed_for_default: bool,
140}
141
142impl RawBytesOptions<'_> {
143    /// Set the database name
144    pub fn name(mut self, name: &str) -> Self {
145        self.name = Some(name.to_string());
146        self
147    }
148
149    /// Use unnamed database for default scope instead of a named database
150    /// This is useful for backward compatibility with existing LMDB databases
151    /// that store data in the unnamed database
152    pub fn unnamed_for_default(mut self) -> Self {
153        self.use_unnamed_for_default = true;
154        self
155    }
156
157    /// Create the database with the current transaction
158    pub fn create(self, txn: &mut RwTxn) -> Result<ScopedBytesDatabase, ScopedDbError> {
159        let name = self
160            .name
161            .ok_or_else(|| ScopedDbError::InvalidInput("Database name is required".into()))?;
162
163        crate::scoped_bytes_database::ScopedBytesDatabase::create(
164            self.env,
165            &name,
166            txn,
167            self.global_registry.clone(),
168            self.use_unnamed_for_default,
169        )
170    }
171}
172
173/// Module-level function to create scoped database options
174pub fn scoped_database_options(
175    env: &Env,
176    global_registry: Arc<GlobalScopeRegistry>,
177) -> ScopedDatabaseOptions {
178    ScopedDatabaseOptions::new(env, global_registry)
179}