420 lines
14 KiB
Rust
420 lines
14 KiB
Rust
use redis::{Client, Connection};
|
|
use std::sync::{Arc, Mutex};
|
|
use crate::error::{DocTreeError, Result};
|
|
|
|
/// Storage backend for doctree
|
|
pub struct RedisStorage {
|
|
// Redis client
|
|
client: Client,
|
|
// Connection pool
|
|
connection: Arc<Mutex<Connection>>,
|
|
// Doctree name for key prefixing
|
|
doctree_name: Arc<Mutex<String>>,
|
|
}
|
|
|
|
impl RedisStorage {
|
|
/// Create a new RedisStorage instance
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `url` - Redis connection URL (e.g., "redis://localhost:6379")
|
|
/// This is ignored in the in-memory implementation
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A new RedisStorage instance or an error
|
|
pub fn new(url: &str) -> Result<Self> {
|
|
// Create a Redis client
|
|
let client = Client::open(url).map_err(|e| DocTreeError::RedisError(format!("Failed to connect to Redis: {}", e)))?;
|
|
|
|
// Get a connection
|
|
let connection = client.get_connection().map_err(|e| DocTreeError::RedisError(format!("Failed to get Redis connection: {}", e)))?;
|
|
|
|
Ok(Self {
|
|
client,
|
|
connection: Arc::new(Mutex::new(connection)),
|
|
doctree_name: Arc::new(Mutex::new("default".to_string())),
|
|
})
|
|
}
|
|
|
|
/// Set the doctree name for key prefixing
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `name` - Doctree name
|
|
pub fn set_doctree_name(&self, name: &str) {
|
|
let mut doctree_name = self.doctree_name.lock().unwrap();
|
|
*doctree_name = name.to_string();
|
|
}
|
|
|
|
/// Get the doctree name
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The doctree name
|
|
pub fn get_doctree_name(&self) -> String {
|
|
let doctree_name = self.doctree_name.lock().unwrap();
|
|
doctree_name.clone()
|
|
}
|
|
|
|
/// Store a collection entry
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
/// * `key` - Entry key
|
|
/// * `value` - Entry value
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Ok(()) on success or an error
|
|
pub fn store_collection_entry(&self, collection: &str, key: &str, value: &str) -> Result<()> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let redis_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - HSET {} {} {}", redis_key, key, value);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Store the entry using HSET
|
|
redis::cmd("HSET")
|
|
.arg(&redis_key)
|
|
.arg(key)
|
|
.arg(value)
|
|
.execute(&mut *conn);
|
|
|
|
println!("DEBUG: Stored entry in Redis - collection: '{}', key: '{}', value: '{}'",
|
|
collection, key, value);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a collection entry
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
/// * `key` - Entry key
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The entry value or an error
|
|
pub fn get_collection_entry(&self, collection: &str, key: &str) -> Result<String> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let collection_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - HGET {} {}", collection_key, key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Get the entry using HGET
|
|
let result: Option<String> = redis::cmd("HGET")
|
|
.arg(&collection_key)
|
|
.arg(key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
// Check if the entry exists
|
|
match result {
|
|
Some(value) => {
|
|
println!("DEBUG: Retrieved entry from Redis - collection: '{}', key: '{}', value: '{}'",
|
|
collection, key, value);
|
|
Ok(value)
|
|
},
|
|
None => {
|
|
println!("DEBUG: Entry not found in Redis - collection: '{}', key: '{}'",
|
|
collection, key);
|
|
Err(DocTreeError::FileNotFound(key.to_string()))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Delete a collection entry
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
/// * `key` - Entry key
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Ok(()) on success or an error
|
|
pub fn delete_collection_entry(&self, collection: &str, key: &str) -> Result<()> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let collection_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - HDEL {} {}", collection_key, key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Delete the entry using HDEL
|
|
let exists: bool = redis::cmd("HEXISTS")
|
|
.arg(&collection_key)
|
|
.arg(key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
if !exists {
|
|
return Err(DocTreeError::CollectionNotFound(collection.to_string()));
|
|
}
|
|
|
|
redis::cmd("HDEL")
|
|
.arg(&collection_key)
|
|
.arg(key)
|
|
.execute(&mut *conn);
|
|
|
|
println!("DEBUG: Deleted entry from Redis - collection: '{}', key: '{}'",
|
|
collection, key);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// List all entries in a collection
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A vector of entry keys or an error
|
|
pub fn list_collection_entries(&self, collection: &str) -> Result<Vec<String>> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let collection_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - HKEYS {}", collection_key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Check if the collection exists
|
|
let exists: bool = redis::cmd("EXISTS")
|
|
.arg(&collection_key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
if !exists {
|
|
return Err(DocTreeError::CollectionNotFound(collection.to_string()));
|
|
}
|
|
|
|
// Get all keys using HKEYS
|
|
let keys: Vec<String> = redis::cmd("HKEYS")
|
|
.arg(&collection_key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
println!("DEBUG: Listed {} entries from Redis - collection: '{}'",
|
|
keys.len(), collection);
|
|
|
|
Ok(keys)
|
|
}
|
|
|
|
/// Delete a collection
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Ok(()) on success or an error
|
|
pub fn delete_collection(&self, collection: &str) -> Result<()> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let redis_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - DEL {}", redis_key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Delete the collection using DEL
|
|
redis::cmd("DEL")
|
|
.arg(&redis_key)
|
|
.execute(&mut *conn);
|
|
|
|
println!("DEBUG: Deleted collection from Redis - collection: '{}'", collection);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if a collection exists
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// true if the collection exists, false otherwise
|
|
pub fn collection_exists(&self, collection: &str) -> Result<bool> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let collection_key = format!("{}:collections:{}", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - EXISTS {}", collection_key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Check if the collection exists using EXISTS
|
|
let exists: bool = redis::cmd("EXISTS")
|
|
.arg(&collection_key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
println!("DEBUG: Collection exists check - collection: '{}', exists: {}",
|
|
collection, exists);
|
|
|
|
Ok(exists)
|
|
}
|
|
|
|
/// List all collections in Redis
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A vector of collection names or an error
|
|
pub fn list_all_collections(&self) -> Result<Vec<String>> {
|
|
let doctree_name = self.get_doctree_name();
|
|
println!("DEBUG: Redis operation - KEYS {}:collections:*", doctree_name);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Get all collection keys
|
|
let pattern = format!("{}:collections:*", doctree_name);
|
|
let keys: Vec<String> = redis::cmd("KEYS")
|
|
.arg(&pattern)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
// Extract collection names from keys (remove the "{doctree_name}:collections:" prefix)
|
|
let prefix = format!("{}:collections:", doctree_name);
|
|
let prefix_len = prefix.len();
|
|
|
|
let collections = keys.iter()
|
|
.filter_map(|key| {
|
|
if key.starts_with(&prefix) {
|
|
Some(key[prefix_len..].to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
println!("DEBUG: Found {} collections in Redis", keys.len());
|
|
|
|
Ok(collections)
|
|
}
|
|
|
|
/// Delete all collections from Redis
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Ok(()) on success or an error
|
|
pub fn delete_all_collections(&self) -> Result<()> {
|
|
let doctree_name = self.get_doctree_name();
|
|
println!("DEBUG: Redis operation - KEYS {}:collections:*", doctree_name);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Get all collection keys
|
|
let pattern = format!("{}:collections:*", doctree_name);
|
|
let keys: Vec<String> = redis::cmd("KEYS")
|
|
.arg(&pattern)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
println!("DEBUG: Found {} collections in Redis", keys.len());
|
|
|
|
// Delete each collection
|
|
for key in keys {
|
|
println!("DEBUG: Redis operation - DEL {}", key);
|
|
redis::cmd("DEL")
|
|
.arg(&key)
|
|
.execute(&mut *conn);
|
|
println!("DEBUG: Deleted collection from Redis - key: '{}'", key);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Store a collection's path
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
/// * `path` - Collection path
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Ok(()) on success or an error
|
|
pub fn store_collection_path(&self, collection: &str, path: &str) -> Result<()> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let redis_key = format!("{}:collections:{}:path", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - SET {} {}", redis_key, path);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Store the path using SET
|
|
redis::cmd("SET")
|
|
.arg(&redis_key)
|
|
.arg(path)
|
|
.execute(&mut *conn);
|
|
|
|
println!("DEBUG: Stored collection path in Redis - collection: '{}', path: '{}'",
|
|
collection, path);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get a collection's path
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `collection` - Collection name
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// The collection path or an error
|
|
pub fn get_collection_path(&self, collection: &str) -> Result<String> {
|
|
let doctree_name = self.get_doctree_name();
|
|
let redis_key = format!("{}:collections:{}:path", doctree_name, collection);
|
|
println!("DEBUG: Redis operation - GET {}", redis_key);
|
|
|
|
// Get a connection from the pool
|
|
let mut conn = self.connection.lock().unwrap();
|
|
|
|
// Get the path using GET
|
|
let result: Option<String> = redis::cmd("GET")
|
|
.arg(&redis_key)
|
|
.query(&mut *conn)
|
|
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
|
|
|
|
// Check if the path exists
|
|
match result {
|
|
Some(path) => {
|
|
println!("DEBUG: Retrieved collection path from Redis - collection: '{}', path: '{}'",
|
|
collection, path);
|
|
Ok(path)
|
|
},
|
|
None => {
|
|
println!("DEBUG: Collection path not found in Redis - collection: '{}'",
|
|
collection);
|
|
Err(DocTreeError::CollectionNotFound(collection.to_string()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Implement Clone for RedisStorage
|
|
impl Clone for RedisStorage {
|
|
fn clone(&self) -> Self {
|
|
// Create a new connection
|
|
let connection = self.client.get_connection()
|
|
.expect("Failed to get Redis connection");
|
|
|
|
Self {
|
|
client: self.client.clone(),
|
|
connection: Arc::new(Mutex::new(connection)),
|
|
doctree_name: self.doctree_name.clone(),
|
|
}
|
|
}
|
|
} |