1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//! Storage abstractions for asset information and registry data.
//!
//! This module provides traits and implementations for persistent storage of asset information
//! and registry data. It defines a common interface for different storage backends while
//! ensuring thread safety and atomic operations.
//!
//! The module provides:
//!
//! - The [`Store`] trait which defines the common interface for all storage implementations
//! - Specialized store implementations for different storage backends
//! - Worker-specific store functionality through the [`WorkerStore`] trait
//!
//! # Storage Capabilities
//!
//! The store is responsible for:
//!
//! - Storing and retrieving the registry and its IPFS hash
//! - Managing asset information with prefix-based organization
//! - Supporting both individual and batch operations for asset data
//!
//! # Extensibility
//!
//! While this module provides general-purpose implementations for common storage backends,
//! the [`Store`] trait is designed for extensibility. Users can create custom storage
//! implementations by implementing this trait to support specialized backends, caching
//! strategies, or data transformation logic beyond what's provided in the standard implementations.
//!
//! # Implementation
//!
//! When implementing a custom store:
//!
//! 1. Implement the [`Store`] trait
//! 2. Define an appropriate `Error` type that captures all possible storage failures
//! 3. Ensure thread safety and atomicity in all operations
//! 4. Make the implementation efficiently cloneable for use across tasks
use Error as StdError;
use async_trait;
pub use WorkerStore;
use crate;
use crateAssetInfo;
/// The universal trait for all stores. All implementations must be thread-safe and atomic.
///
/// This trait defines the common interface that all storage implementations must provide,
/// enabling the system to interact with different storage backends in a uniform way.
/// Each store is responsible for reliably persisting asset information and registry data.
///
/// # Thread Safety and Atomicity
///
/// All implementations must be thread-safe (Send + Sync) and provide atomic operations
/// to ensure data consistency when used concurrently.
///
/// # Required Methods
///
/// Implementors must define:
/// * Storage operations for registry data (`set_registry`, `get_registry`, `get_registry_ipfs_hash`)
/// * Asset information operations (`get_asset_info`, `insert_asset_info`, `insert_batch_asset_info`)
///
/// # Example Use
///
/// Implementing a simple in-memory store:
///
/// ```rust
/// use std::collections::HashMap;
/// use std::error::Error;
/// use std::fmt;
/// use std::sync::{Arc, RwLock};
/// use async_trait::async_trait;
/// use bothan_lib::store::Store;
/// use bothan_lib::registry::{Registry, Valid};
/// use bothan_lib::types::AssetInfo;
/// use thiserror::Error;
///
/// // Custom error type for the memory store
/// #[derive(Debug, Error)]
/// #[error("Memory store error: {0}")]
/// struct MemoryStoreError(String);
///
/// // Memory-based store implementation
/// #[derive(Clone)]
/// struct MemoryStore {
/// registry: Arc<RwLock<Option<(Registry<Valid>, String)>>>,
/// assets: Arc<RwLock<HashMap<String, HashMap<String, AssetInfo>>>>,
/// }
///
/// impl MemoryStore {
/// fn new() -> Self {
/// MemoryStore {
/// registry: Arc::new(RwLock::new(None)),
/// assets: Arc::new(RwLock::new(HashMap::new())),
/// }
/// }
/// }
///
/// #[async_trait]
/// impl Store for MemoryStore {
/// type Error = MemoryStoreError;
///
/// async fn set_registry(
/// &self,
/// registry: Registry<Valid>,
/// ipfs_hash: String,
/// ) -> Result<(), Self::Error> {
/// let mut reg = self.registry.write().map_err(|e| MemoryStoreError(e.to_string()))?;
/// *reg = Some((registry, ipfs_hash));
/// Ok(())
/// }
///
/// async fn get_registry(&self) -> Result<Registry<Valid>, Self::Error> {
/// let reg = self.registry.read().map_err(|e| MemoryStoreError(e.to_string()))?;
/// match reg.as_ref() {
/// Some((registry, _)) => Ok(registry.clone()),
/// None => Err(MemoryStoreError("Registry not found".to_string())),
/// }
/// }
///
/// async fn get_registry_ipfs_hash(&self) -> Result<Option<String>, Self::Error> {
/// let reg = self.registry.read().map_err(|e| MemoryStoreError(e.to_string()))?;
/// Ok(reg.as_ref().map(|(_, hash)| hash.clone()))
/// }
///
/// async fn get_asset_info(
/// &self,
/// prefix: &str,
/// id: &str,
/// ) -> Result<Option<AssetInfo>, Self::Error> {
/// let assets = self.assets.read().map_err(|e| MemoryStoreError(e.to_string()))?;
/// Ok(assets
/// .get(prefix)
/// .and_then(|prefix_map| prefix_map.get(id).cloned()))
/// }
///
/// async fn insert_asset_info(
/// &self,
/// prefix: &str,
/// asset_info: AssetInfo,
/// ) -> Result<(), Self::Error> {
/// let mut assets = self.assets.write().map_err(|e| MemoryStoreError(e.to_string()))?;
/// let prefix_map = assets.entry(prefix.to_string()).or_insert_with(HashMap::new);
/// prefix_map.insert(asset_info.id.clone(), asset_info);
/// Ok(())
/// }
///
/// async fn insert_batch_asset_info(
/// &self,
/// prefix: &str,
/// asset_infos: Vec<AssetInfo>,
/// ) -> Result<(), Self::Error> {
/// let mut assets = self.assets.write().map_err(|e| MemoryStoreError(e.to_string()))?;
/// let prefix_map = assets.entry(prefix.to_string()).or_insert_with(HashMap::new);
///
/// for asset_info in asset_infos {
/// prefix_map.insert(asset_info.id.clone(), asset_info);
/// }
/// Ok(())
/// }
/// }
/// ```