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>, // Doctree name for key prefixing doctree_name: Arc>, } 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 { // 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 { 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 = 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> { 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 = 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 { 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> { 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 = 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 = 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 { 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 = 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(), } } }