doctree_rust/doctree/src/storage.rs
2025-04-09 08:42:30 +02:00

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(),
}
}
}