.. | ||
error.rs | ||
mod.rs | ||
README.md | ||
store.rs |
Key-Value Store (KVS) Module
This module provides a simple key-value store implementation with dual backends:
- IndexedDB for WebAssembly applications running in browsers
- In-memory storage for testing and non-browser environments
Overview
The KVS module provides a simple, yet powerful interface for storing and retrieving data. In a browser environment, it uses IndexedDB as the underlying storage mechanism, which provides a robust, persistent storage solution that works offline and can handle large amounts of data. In non-browser environments, it uses an in-memory store for testing purposes.
Features
- Simple API: Easy-to-use methods for common operations like get, set, delete
- Type Safety: Generic methods that preserve your data types through serialization/deserialization
- Error Handling: Comprehensive error types for robust error handling
- Async/Await: Modern async interface for all operations
- Serialization: Automatic serialization/deserialization of complex data types
Core Components
KvsStore
The main struct that provides access to the key-value store, with different implementations based on the environment:
// In WebAssembly environments (browsers)
pub struct KvsStore {
db: Arc<Database>,
store_name: String,
}
// In non-WebAssembly environments (for testing)
pub struct KvsStore {
data: Arc<Mutex<HashMap<String, String>>>,
}
Error Types
The module defines several error types to handle different failure scenarios:
pub enum KvsError {
Idb(String),
KeyNotFound(String),
Serialization(String),
Deserialization(String),
Other(String),
}
Usage Examples
Opening a Store
let store = KvsStore::open("my_database", "my_store").await?;
Storing Values
// Store a simple string
store.set("string_key", &"Hello, world!").await?;
// Store a complex object
let user = User {
id: 1,
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
};
store.set("user_1", &user).await?;
Retrieving Values
// Get a string
let value: String = store.get("string_key").await?;
// Get a complex object
let user: User = store.get("user_1").await?;
Checking if a Key Exists
if store.contains("user_1").await? {
// Key exists
}
Deleting Values
store.delete("user_1").await?;
Listing All Keys
let keys = store.keys().await?;
for key in keys {
println!("Found key: {}", key);
}
Clearing the Store
store.clear().await?;
Error Handling
The module uses a custom Result
type that wraps KvsError
:
type Result<T> = std::result::Result<T, KvsError>;
Example of error handling:
match store.get::<User>("nonexistent_key").await {
Ok(user) => {
// Process user
},
Err(KvsError::KeyNotFound(key)) => {
println!("Key not found: {}", key);
},
Err(e) => {
println!("An error occurred: {}", e);
}
}
Implementation Details
The KVS module uses:
- Dual backend architecture:
- IndexedDB for browser environments via the
idb
crate (direct Rust implementation) - In-memory HashMap for testing and non-browser environments
- IndexedDB for browser environments via the
- Conditional compilation with
#[cfg(target_arch = "wasm32")]
to select the appropriate implementation - Serde for serialization/deserialization
- Wasm-bindgen for JavaScript interop in browser environments
- Async/await for non-blocking operations
- Arc and Mutex for thread-safe access to the in-memory store
Note: This implementation uses the idb
crate to interact with IndexedDB directly from Rust, eliminating the need for a JavaScript bridge file.
Testing
The module includes comprehensive tests in src/tests/kvs_tests.rs
that verify all functionality works as expected.
Running the Tests
Thanks to the dual implementation, tests can be run in two ways:
Standard Rust Tests
The in-memory implementation allows tests to run in a standard Rust environment without requiring a browser:
cargo test
This runs all tests using the in-memory implementation, which is perfect for CI/CD pipelines and quick development testing.
WebAssembly Tests in Browser
For testing the actual IndexedDB implementation, you can use wasm-bindgen-test
to run tests in a browser environment:
-
Install wasm-pack if you haven't already:
cargo install wasm-pack
-
Run the tests in a headless browser:
wasm-pack test --headless --firefox
You can also use Chrome or Safari:
wasm-pack test --headless --chrome wasm-pack test --headless --safari
-
Run tests in a browser with a UI (for debugging):
wasm-pack test --firefox
-
Run specific tests:
wasm-pack test --firefox -- --filter kvs_tests
Test Structure
The tests are organized to test each functionality of the KVS module:
- Basic Operations: Tests for opening a store, setting/getting values
- Complex Data: Tests for storing and retrieving complex objects
- Error Handling: Tests for handling nonexistent keys and errors
- Management Operations: Tests for listing keys, checking existence, and clearing the store
Each test follows a pattern:
- Set up the test environment
- Perform the operation being tested
- Verify the results
- Clean up after the test
In-Memory Implementation
The module includes a built-in in-memory implementation that is automatically used in non-WebAssembly environments. This implementation:
- Uses a
HashMap<String, String>
wrapped inArc<Mutex<>>
for thread safety - Provides the same API as the IndexedDB implementation
- Automatically serializes/deserializes values using serde_json
- Makes testing much easier by eliminating the need for a browser environment
This dual implementation approach means you don't need to create separate mocks for testing - the module handles this automatically through conditional compilation.