Compare commits

..

4 Commits

Author SHA1 Message Date
84c656983a ... 2025-04-09 08:45:38 +02:00
19f52a8172 ... 2025-04-09 08:43:20 +02:00
14b2bb2798 ... 2025-04-09 08:43:10 +02:00
2eec3be632 ... 2025-04-09 08:42:30 +02:00
6 changed files with 274 additions and 224 deletions

View File

@ -38,6 +38,9 @@ pub struct DocTree {
/// Redis storage backend /// Redis storage backend
storage: RedisStorage, storage: RedisStorage,
/// Name of the doctree (used as prefix for Redis keys)
pub doctree_name: String,
/// For backward compatibility /// For backward compatibility
pub name: String, pub name: String,
@ -56,6 +59,9 @@ pub struct DocTreeBuilder {
/// Redis storage backend /// Redis storage backend
storage: Option<RedisStorage>, storage: Option<RedisStorage>,
/// Name of the doctree (used as prefix for Redis keys)
doctree_name: Option<String>,
/// For backward compatibility /// For backward compatibility
name: Option<String>, name: Option<String>,
@ -74,6 +80,7 @@ impl DocTree {
collections: HashMap::new(), collections: HashMap::new(),
default_collection: None, default_collection: None,
storage: None, storage: None,
doctree_name: Some("default".to_string()),
name: None, name: None,
path: None, path: None,
} }
@ -92,8 +99,12 @@ impl DocTree {
pub fn add_collection<P: AsRef<Path>>(&mut self, path: P, name: &str) -> Result<&Collection> { pub fn add_collection<P: AsRef<Path>>(&mut self, path: P, name: &str) -> Result<&Collection> {
// Create a new collection // Create a new collection
let namefixed = name_fix(name); let namefixed = name_fix(name);
// Clone the storage and set the doctree name
let storage = self.storage.clone();
storage.set_doctree_name(&self.doctree_name);
let collection = Collection::builder(path, &namefixed) let collection = Collection::builder(path, &namefixed)
.with_storage(self.storage.clone()) .with_storage(storage)
.build()?; .build()?;
// Scan the collection // Scan the collection
@ -531,6 +542,20 @@ impl DocTreeBuilder {
/// # Returns /// # Returns
/// ///
/// Self for method chaining /// Self for method chaining
/// Set the doctree name
///
/// # Arguments
///
/// * `name` - Name of the doctree
///
/// # Returns
///
/// Self for method chaining
pub fn with_doctree_name(mut self, name: &str) -> Self {
self.doctree_name = Some(name.to_string());
self
}
pub fn with_storage(mut self, storage: RedisStorage) -> Self { pub fn with_storage(mut self, storage: RedisStorage) -> Self {
self.storage = Some(storage); self.storage = Some(storage);
self self
@ -552,10 +577,18 @@ impl DocTreeBuilder {
DocTreeError::MissingParameter("storage".to_string()) DocTreeError::MissingParameter("storage".to_string())
})?; })?;
// Get the doctree name
let doctree_name = self.doctree_name.clone().unwrap_or_else(|| "default".to_string());
// Create a new collection // Create a new collection
let namefixed = name_fix(name); let namefixed = name_fix(name);
// Clone the storage and set the doctree name
let storage_clone = storage.clone();
storage_clone.set_doctree_name(&doctree_name);
let collection = Collection::builder(path.as_ref(), &namefixed) let collection = Collection::builder(path.as_ref(), &namefixed)
.with_storage(storage.clone()) .with_storage(storage_clone)
.build()?; .build()?;
// Scan the collection // Scan the collection
@ -604,11 +637,19 @@ impl DocTreeBuilder {
DocTreeError::MissingParameter("storage".to_string()) DocTreeError::MissingParameter("storage".to_string())
})?; })?;
// Get the doctree name
let doctree_name = self.doctree_name.clone().unwrap_or_else(|| "default".to_string());
// Clone the storage and set the doctree name
let storage_clone = storage.clone();
storage_clone.set_doctree_name(&doctree_name);
// Create a temporary DocTree to scan collections // Create a temporary DocTree to scan collections
let mut temp_doctree = DocTree { let mut temp_doctree = DocTree {
collections: HashMap::new(), collections: HashMap::new(),
default_collection: None, default_collection: None,
storage: storage.clone(), storage: storage_clone,
doctree_name: doctree_name,
name: self.name.clone().unwrap_or_default(), name: self.name.clone().unwrap_or_default(),
path: self.path.clone().unwrap_or_else(|| PathBuf::from("")), path: self.path.clone().unwrap_or_else(|| PathBuf::from("")),
}; };
@ -641,11 +682,19 @@ impl DocTreeBuilder {
DocTreeError::MissingParameter("storage".to_string()) DocTreeError::MissingParameter("storage".to_string())
})?; })?;
// Get the doctree name
let doctree_name = self.doctree_name.unwrap_or_else(|| "default".to_string());
// Set the doctree name in the storage
let storage_clone = storage.clone();
storage_clone.set_doctree_name(&doctree_name);
// Create the DocTree // Create the DocTree
let mut doctree = DocTree { let mut doctree = DocTree {
collections: self.collections, collections: self.collections,
default_collection: self.default_collection, default_collection: self.default_collection,
storage: storage.clone(), storage: storage_clone,
doctree_name,
name: self.name.unwrap_or_default(), name: self.name.unwrap_or_default(),
path: self.path.unwrap_or_else(|| PathBuf::from("")), path: self.path.unwrap_or_else(|| PathBuf::from("")),
}; };
@ -684,6 +733,12 @@ pub fn new<P: AsRef<Path>>(args: &[&str]) -> Result<DocTree> {
let mut builder = DocTree::builder().with_storage(storage); let mut builder = DocTree::builder().with_storage(storage);
// If the first argument is a doctree name, use it
if args.len() >= 1 && args[0].starts_with("--doctree=") {
let doctree_name = args[0].trim_start_matches("--doctree=");
builder = builder.with_doctree_name(doctree_name);
}
// For backward compatibility with existing code // For backward compatibility with existing code
if args.len() == 2 { if args.len() == 2 {
let path = args[0]; let path = args[0];
@ -707,15 +762,20 @@ pub fn new<P: AsRef<Path>>(args: &[&str]) -> Result<DocTree> {
/// # Arguments /// # Arguments
/// ///
/// * `root_path` - The root path to scan for collections /// * `root_path` - The root path to scan for collections
/// * `doctree_name` - Optional name for the doctree (default: "default")
/// ///
/// # Returns /// # Returns
/// ///
/// A new DocTree or an error /// A new DocTree or an error
pub fn from_directory<P: AsRef<Path>>(root_path: P) -> Result<DocTree> { pub fn from_directory<P: AsRef<Path>>(root_path: P, doctree_name: Option<&str>) -> Result<DocTree> {
let storage = RedisStorage::new("redis://localhost:6379")?; let storage = RedisStorage::new("redis://localhost:6379")?;
DocTree::builder() let mut builder = DocTree::builder().with_storage(storage);
.with_storage(storage)
.scan_collections(root_path)? // Set the doctree name if provided
.build() if let Some(name) = doctree_name {
builder = builder.with_doctree_name(name);
}
builder.scan_collections(root_path)?.build()
} }

View File

@ -22,7 +22,7 @@ pub use include::process_includes;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::path::Path;
#[test] #[test]
fn test_doctree_builder() { fn test_doctree_builder() {

View File

@ -8,6 +8,8 @@ pub struct RedisStorage {
client: Client, client: Client,
// Connection pool // Connection pool
connection: Arc<Mutex<Connection>>, connection: Arc<Mutex<Connection>>,
// Doctree name for key prefixing
doctree_name: Arc<Mutex<String>>,
} }
impl RedisStorage { impl RedisStorage {
@ -31,9 +33,30 @@ impl RedisStorage {
Ok(Self { Ok(Self {
client, client,
connection: Arc::new(Mutex::new(connection)), 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 /// Store a collection entry
/// ///
/// # Arguments /// # Arguments
@ -46,7 +69,8 @@ impl RedisStorage {
/// ///
/// Ok(()) on success or an error /// Ok(()) on success or an error
pub fn store_collection_entry(&self, collection: &str, key: &str, value: &str) -> Result<()> { pub fn store_collection_entry(&self, collection: &str, key: &str, value: &str) -> Result<()> {
let redis_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let redis_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - HSET {} {} {}", redis_key, key, value); println!("DEBUG: Redis operation - HSET {} {} {}", redis_key, key, value);
// Get a connection from the pool // Get a connection from the pool
@ -76,7 +100,8 @@ impl RedisStorage {
/// ///
/// The entry value or an error /// The entry value or an error
pub fn get_collection_entry(&self, collection: &str, key: &str) -> Result<String> { pub fn get_collection_entry(&self, collection: &str, key: &str) -> Result<String> {
let collection_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let collection_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - HGET {} {}", collection_key, key); println!("DEBUG: Redis operation - HGET {} {}", collection_key, key);
// Get a connection from the pool // Get a connection from the pool
@ -115,7 +140,8 @@ impl RedisStorage {
/// ///
/// Ok(()) on success or an error /// Ok(()) on success or an error
pub fn delete_collection_entry(&self, collection: &str, key: &str) -> Result<()> { pub fn delete_collection_entry(&self, collection: &str, key: &str) -> Result<()> {
let collection_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let collection_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - HDEL {} {}", collection_key, key); println!("DEBUG: Redis operation - HDEL {} {}", collection_key, key);
// Get a connection from the pool // Get a connection from the pool
@ -153,7 +179,8 @@ impl RedisStorage {
/// ///
/// A vector of entry keys or an error /// A vector of entry keys or an error
pub fn list_collection_entries(&self, collection: &str) -> Result<Vec<String>> { pub fn list_collection_entries(&self, collection: &str) -> Result<Vec<String>> {
let collection_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let collection_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - HKEYS {}", collection_key); println!("DEBUG: Redis operation - HKEYS {}", collection_key);
// Get a connection from the pool // Get a connection from the pool
@ -191,7 +218,8 @@ impl RedisStorage {
/// ///
/// Ok(()) on success or an error /// Ok(()) on success or an error
pub fn delete_collection(&self, collection: &str) -> Result<()> { pub fn delete_collection(&self, collection: &str) -> Result<()> {
let redis_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let redis_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - DEL {}", redis_key); println!("DEBUG: Redis operation - DEL {}", redis_key);
// Get a connection from the pool // Get a connection from the pool
@ -217,7 +245,8 @@ impl RedisStorage {
/// ///
/// true if the collection exists, false otherwise /// true if the collection exists, false otherwise
pub fn collection_exists(&self, collection: &str) -> Result<bool> { pub fn collection_exists(&self, collection: &str) -> Result<bool> {
let collection_key = format!("collections:{}", collection); let doctree_name = self.get_doctree_name();
let collection_key = format!("{}:collections:{}", doctree_name, collection);
println!("DEBUG: Redis operation - EXISTS {}", collection_key); println!("DEBUG: Redis operation - EXISTS {}", collection_key);
// Get a connection from the pool // Get a connection from the pool
@ -241,22 +270,27 @@ impl RedisStorage {
/// ///
/// A vector of collection names or an error /// A vector of collection names or an error
pub fn list_all_collections(&self) -> Result<Vec<String>> { pub fn list_all_collections(&self) -> Result<Vec<String>> {
println!("DEBUG: Redis operation - KEYS collections:*"); let doctree_name = self.get_doctree_name();
println!("DEBUG: Redis operation - KEYS {}:collections:*", doctree_name);
// Get a connection from the pool // Get a connection from the pool
let mut conn = self.connection.lock().unwrap(); let mut conn = self.connection.lock().unwrap();
// Get all collection keys // Get all collection keys
let pattern = format!("{}:collections:*", doctree_name);
let keys: Vec<String> = redis::cmd("KEYS") let keys: Vec<String> = redis::cmd("KEYS")
.arg("collections:*") .arg(&pattern)
.query(&mut *conn) .query(&mut *conn)
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?; .map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
// Extract collection names from keys (remove the "collections:" prefix) // 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() let collections = keys.iter()
.filter_map(|key| { .filter_map(|key| {
if key.starts_with("collections:") { if key.starts_with(&prefix) {
Some(key[12..].to_string()) Some(key[prefix_len..].to_string())
} else { } else {
None None
} }
@ -274,14 +308,16 @@ impl RedisStorage {
/// ///
/// Ok(()) on success or an error /// Ok(()) on success or an error
pub fn delete_all_collections(&self) -> Result<()> { pub fn delete_all_collections(&self) -> Result<()> {
println!("DEBUG: Redis operation - KEYS collections:*"); let doctree_name = self.get_doctree_name();
println!("DEBUG: Redis operation - KEYS {}:collections:*", doctree_name);
// Get a connection from the pool // Get a connection from the pool
let mut conn = self.connection.lock().unwrap(); let mut conn = self.connection.lock().unwrap();
// Get all collection keys // Get all collection keys
let pattern = format!("{}:collections:*", doctree_name);
let keys: Vec<String> = redis::cmd("KEYS") let keys: Vec<String> = redis::cmd("KEYS")
.arg("collections:*") .arg(&pattern)
.query(&mut *conn) .query(&mut *conn)
.map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?; .map_err(|e| DocTreeError::RedisError(format!("Redis error: {}", e)))?;
@ -298,6 +334,7 @@ impl RedisStorage {
Ok(()) Ok(())
} }
/// Store a collection's path /// Store a collection's path
/// ///
/// # Arguments /// # Arguments
@ -309,7 +346,8 @@ impl RedisStorage {
/// ///
/// Ok(()) on success or an error /// Ok(()) on success or an error
pub fn store_collection_path(&self, collection: &str, path: &str) -> Result<()> { pub fn store_collection_path(&self, collection: &str, path: &str) -> Result<()> {
let redis_key = format!("collections:{}:path", collection); let doctree_name = self.get_doctree_name();
let redis_key = format!("{}:collections:{}:path", doctree_name, collection);
println!("DEBUG: Redis operation - SET {} {}", redis_key, path); println!("DEBUG: Redis operation - SET {} {}", redis_key, path);
// Get a connection from the pool // Get a connection from the pool
@ -337,7 +375,8 @@ impl RedisStorage {
/// ///
/// The collection path or an error /// The collection path or an error
pub fn get_collection_path(&self, collection: &str) -> Result<String> { pub fn get_collection_path(&self, collection: &str) -> Result<String> {
let redis_key = format!("collections:{}:path", collection); let doctree_name = self.get_doctree_name();
let redis_key = format!("{}:collections:{}:path", doctree_name, collection);
println!("DEBUG: Redis operation - GET {}", redis_key); println!("DEBUG: Redis operation - GET {}", redis_key);
// Get a connection from the pool // Get a connection from the pool
@ -375,6 +414,7 @@ impl Clone for RedisStorage {
Self { Self {
client: self.client.clone(), client: self.client.clone(),
connection: Arc::new(Mutex::new(connection)), connection: Arc::new(Mutex::new(connection)),
doctree_name: self.doctree_name.clone(),
} }
} }
} }

View File

@ -9,29 +9,20 @@ fn main() -> Result<()> {
.about("A tool to manage document collections") .about("A tool to manage document collections")
.subcommand( .subcommand(
SubCommand::with_name("scan") SubCommand::with_name("scan")
.about("Scan a directory and create a collection") .about("Scan a directory for .collection files and create collections")
.arg(Arg::with_name("path").required(true).help("Path to the directory")) .arg(Arg::with_name("path").required(true).help("Path to the directory"))
.arg(Arg::with_name("name").required(true).help("Name of the collection")), .arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.subcommand( .subcommand(
SubCommand::with_name("list") SubCommand::with_name("list")
.about("List collections"), .about("List collections")
) .arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
.subcommand(
SubCommand::with_name("scan-collections")
.about("Recursively scan directories for .collection files")
.arg(Arg::with_name("path").required(true).help("Root path to scan for collections")),
)
.subcommand(
SubCommand::with_name("scan-and-info")
.about("Scan collections and show detailed information")
.arg(Arg::with_name("path").required(true).help("Root path to scan for collections"))
.arg(Arg::with_name("collection").help("Name of the collection (optional)")),
) )
.subcommand( .subcommand(
SubCommand::with_name("info") SubCommand::with_name("info")
.about("Show detailed information about collections") .about("Show detailed information about collections")
.arg(Arg::with_name("collection").help("Name of the collection (optional)")), .arg(Arg::with_name("collection").help("Name of the collection (optional)"))
.arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.subcommand( .subcommand(
SubCommand::with_name("get") SubCommand::with_name("get")
@ -51,87 +42,39 @@ fn main() -> Result<()> {
.short("f".chars().next().unwrap()) .short("f".chars().next().unwrap())
.long("format") .long("format")
.takes_value(true) .takes_value(true)
.help("Output format (html or markdown, default: markdown)")), .help("Output format (html or markdown, default: markdown)"))
.arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.subcommand( .subcommand(
SubCommand::with_name("html") SubCommand::with_name("html")
.about("Get page content as HTML") .about("Get page content as HTML")
.arg(Arg::with_name("collection").required(true).help("Name of the collection")) .arg(Arg::with_name("collection").required(true).help("Name of the collection"))
.arg(Arg::with_name("page").required(true).help("Name of the page")), .arg(Arg::with_name("page").required(true).help("Name of the page"))
.arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.subcommand( .subcommand(
SubCommand::with_name("delete-collection") SubCommand::with_name("delete-collection")
.about("Delete a collection from Redis") .about("Delete a collection from Redis")
.arg(Arg::with_name("collection").required(true).help("Name of the collection")), .arg(Arg::with_name("collection").required(true).help("Name of the collection"))
.arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.subcommand( .subcommand(
SubCommand::with_name("reset") SubCommand::with_name("reset")
.about("Delete all collections from Redis"), .about("Delete all collections from Redis")
.arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")),
) )
.get_matches(); .get_matches();
// Create a Redis storage instance
let storage = RedisStorage::new("redis://localhost:6379")?;
// Create a DocTree instance
let mut doctree = DocTree::builder()
.with_storage(storage)
.build()?;
// Handle subcommands // Handle subcommands
if let Some(matches) = matches.subcommand_matches("scan") { if let Some(matches) = matches.subcommand_matches("scan") {
let path = matches.value_of("path").unwrap(); let path = matches.value_of("path").unwrap();
let name = matches.value_of("name").unwrap(); let doctree_name = matches.value_of("doctree").unwrap_or("default");
println!("Scanning directory: {}", path);
doctree.add_collection(Path::new(path), name)?;
println!("Collection '{}' created successfully", name);
} else if let Some(_) = matches.subcommand_matches("list") {
let collections = doctree.list_collections();
if collections.is_empty() {
println!("No collections found");
} else {
println!("Collections:");
for collection in collections {
println!("- {}", collection);
}
}
} else if let Some(matches) = matches.subcommand_matches("get") {
let collection = matches.value_of("collection");
let page = matches.value_of("page").unwrap();
let format = matches.value_of("format").unwrap_or("markdown");
if format.to_lowercase() == "html" {
let html = doctree.page_get_html(collection, page)?;
println!("{}", html);
} else {
let content = doctree.page_get(collection, page)?;
println!("{}", content);
}
} else if let Some(matches) = matches.subcommand_matches("html") {
let collection = matches.value_of("collection").unwrap();
let page = matches.value_of("page").unwrap();
let html = doctree.page_get_html(Some(collection), page)?;
println!("{}", html);
} else if let Some(matches) = matches.subcommand_matches("delete-collection") {
let collection = matches.value_of("collection").unwrap();
println!("Deleting collection '{}' from Redis...", collection);
doctree.delete_collection(collection)?;
println!("Collection '{}' deleted successfully", collection);
} else if let Some(_) = matches.subcommand_matches("reset") {
println!("Deleting all collections from Redis...");
doctree.delete_all_collections()?;
println!("All collections deleted successfully");
} else if let Some(matches) = matches.subcommand_matches("scan-collections") {
let path = matches.value_of("path").unwrap();
println!("Recursively scanning for collections in: {}", path); println!("Recursively scanning for collections in: {}", path);
println!("Using doctree name: {}", doctree_name);
// Use the from_directory function to create a DocTree with all collections // Use the from_directory function to create a DocTree with all collections
let doctree = from_directory(Path::new(path))?; let doctree = from_directory(Path::new(path), Some(doctree_name))?;
// Print the discovered collections // Print the discovered collections
let collections = doctree.list_collections(); let collections = doctree.list_collections();
@ -143,28 +86,92 @@ fn main() -> Result<()> {
println!("- {}", collection); println!("- {}", collection);
} }
} }
} else if let Some(matches) = matches.subcommand_matches("scan-and-info") { } else if let Some(matches) = matches.subcommand_matches("list") {
let path = matches.value_of("path").unwrap(); let doctree_name = matches.value_of("doctree").unwrap_or("default");
let collection_name = matches.value_of("collection");
println!("Recursively scanning for collections in: {}", path); // Create a storage with the specified doctree name
let storage = RedisStorage::new("redis://localhost:6379")?;
storage.set_doctree_name(doctree_name);
// Use the from_directory function to create a DocTree with all collections // Create a DocTree with the specified doctree name
let doctree = from_directory(Path::new(path))?; let doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
// Print the discovered collections
let collections = doctree.list_collections(); let collections = doctree.list_collections();
if collections.is_empty() { if collections.is_empty() {
println!("No collections found"); println!("No collections found in doctree '{}'", doctree_name);
return Ok(()); } else {
println!("Collections in doctree '{}':", doctree_name);
for collection in collections {
println!("- {}", collection);
}
} }
} else if let Some(matches) = matches.subcommand_matches("get") {
let collection = matches.value_of("collection");
let page = matches.value_of("page").unwrap();
let format = matches.value_of("format").unwrap_or("markdown");
let doctree_name = matches.value_of("doctree").unwrap_or("default");
println!("Discovered collections:"); // Create a storage with the specified doctree name
for collection in &collections { let storage = RedisStorage::new("redis://localhost:6379")?;
println!("- {}", collection); storage.set_doctree_name(doctree_name);
// Create a DocTree with the specified doctree name
let mut doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
// Load collections from Redis
doctree.load_collections_from_redis()?;
if format.to_lowercase() == "html" {
let html = doctree.page_get_html(collection, page)?;
println!("{}", html);
} else {
let content = doctree.page_get(collection, page)?;
println!("{}", content);
} }
} else if let Some(matches) = matches.subcommand_matches("html") {
let collection = matches.value_of("collection").unwrap();
let page = matches.value_of("page").unwrap();
let doctree_name = matches.value_of("doctree").unwrap_or("default");
println!("\nDetailed Collection Information:"); // Create a storage with the specified doctree name
let storage = RedisStorage::new("redis://localhost:6379")?;
storage.set_doctree_name(doctree_name);
// Create a DocTree with the specified doctree name
let mut doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
// Load collections from Redis
doctree.load_collections_from_redis()?;
let html = doctree.page_get_html(Some(collection), page)?;
println!("{}", html);
} else if let Some(matches) = matches.subcommand_matches("info") {
let doctree_name = matches.value_of("doctree").unwrap_or("default");
// Create a storage with the specified doctree name
let storage = RedisStorage::new("redis://localhost:6379")?;
storage.set_doctree_name(doctree_name);
// Create a DocTree with the specified doctree name
let mut doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
// Load collections from Redis
doctree.load_collections_from_redis()?;
let collection_name = matches.value_of("collection");
if let Some(name) = collection_name { if let Some(name) = collection_name {
// Show info for a specific collection // Show info for a specific collection
@ -172,7 +179,7 @@ fn main() -> Result<()> {
Ok(collection) => { Ok(collection) => {
println!("Collection Information for '{}':", name); println!("Collection Information for '{}':", name);
println!(" Path: {:?}", collection.path); println!(" Path: {:?}", collection.path);
println!(" Redis Key: collections:{}", collection.name); println!(" Redis Key: {}:collections:{}", doctree_name, collection.name);
// List documents // List documents
match collection.page_list() { match collection.page_list() {
@ -181,7 +188,7 @@ fn main() -> Result<()> {
for page in pages { for page in pages {
match collection.page_get_path(&page) { match collection.page_get_path(&page) {
Ok(path) => { Ok(path) => {
println!(" - {} => Redis: collections:{} / {}", path, collection.name, page); println!(" - {} => Redis: {}:collections:{} / {}", path, doctree_name, collection.name, page);
}, },
Err(_) => { Err(_) => {
println!(" - {}", page); println!(" - {}", page);
@ -206,7 +213,7 @@ fn main() -> Result<()> {
println!(" Images ({}):", images.len()); println!(" Images ({}):", images.len());
for image in images { for image in images {
println!(" - {} => Redis: collections:{} / {}", image, collection.name, image); println!(" - {} => Redis: {}:collections:{} / {}", image, doctree_name, collection.name, image);
} }
// Filter other files // Filter other files
@ -220,97 +227,7 @@ fn main() -> Result<()> {
println!(" Other Files ({}):", other_files.len()); println!(" Other Files ({}):", other_files.len());
for file in other_files { for file in other_files {
println!(" - {} => Redis: collections:{} / {}", file, collection.name, file); println!(" - {} => Redis: {}:collections:{} / {}", file, doctree_name, collection.name, file);
}
},
Err(e) => println!(" Error listing files: {}", e),
}
},
Err(e) => println!("Error: {}", e),
}
} else {
// Show info for all collections
for name in collections {
if let Ok(collection) = doctree.get_collection(&name) {
println!("- {} (Redis Key: collections:{})", name, collection.name);
println!(" Path: {:?}", collection.path);
// Count documents and images
if let Ok(pages) = collection.page_list() {
println!(" Documents: {}", pages.len());
}
if let Ok(files) = collection.file_list() {
let image_count = files.iter()
.filter(|f|
f.ends_with(".png") || f.ends_with(".jpg") ||
f.ends_with(".jpeg") || f.ends_with(".gif") ||
f.ends_with(".svg"))
.count();
println!(" Images: {}", image_count);
println!(" Other Files: {}", files.len() - image_count);
}
}
}
}
} else if let Some(matches) = matches.subcommand_matches("info") {
let collection_name = matches.value_of("collection");
if let Some(name) = collection_name {
// Show info for a specific collection
match doctree.get_collection(name) {
Ok(collection) => {
println!("Collection Information for '{}':", name);
println!(" Path: {:?}", collection.path);
println!(" Redis Key: collections:{}", collection.name);
// List documents
match collection.page_list() {
Ok(pages) => {
println!(" Documents ({}):", pages.len());
for page in pages {
match collection.page_get_path(&page) {
Ok(path) => {
println!(" - {} => Redis: collections:{} / {}", path, collection.name, page);
},
Err(_) => {
println!(" - {}", page);
}
}
}
},
Err(e) => println!(" Error listing documents: {}", e),
}
// List files
match collection.file_list() {
Ok(files) => {
// Filter images
let images: Vec<String> = files.iter()
.filter(|f|
f.ends_with(".png") || f.ends_with(".jpg") ||
f.ends_with(".jpeg") || f.ends_with(".gif") ||
f.ends_with(".svg"))
.cloned()
.collect();
println!(" Images ({}):", images.len());
for image in images {
println!(" - {} => Redis: collections:{} / {}", image, collection.name, image);
}
// Filter other files
let other_files: Vec<String> = files.iter()
.filter(|f|
!f.ends_with(".png") && !f.ends_with(".jpg") &&
!f.ends_with(".jpeg") && !f.ends_with(".gif") &&
!f.ends_with(".svg"))
.cloned()
.collect();
println!(" Other Files ({}):", other_files.len());
for file in other_files {
println!(" - {} => Redis: collections:{} / {}", file, collection.name, file);
} }
}, },
Err(e) => println!(" Error listing files: {}", e), Err(e) => println!(" Error listing files: {}", e),
@ -324,10 +241,10 @@ fn main() -> Result<()> {
if collections.is_empty() { if collections.is_empty() {
println!("No collections found"); println!("No collections found");
} else { } else {
println!("Collections:"); println!("Collections in doctree '{}':", doctree_name);
for name in collections { for name in collections {
if let Ok(collection) = doctree.get_collection(&name) { if let Ok(collection) = doctree.get_collection(&name) {
println!("- {} (Redis Key: collections:{})", name, collection.name); println!("- {} (Redis Key: {}:collections:{})", name, doctree_name, collection.name);
println!(" Path: {:?}", collection.path); println!(" Path: {:?}", collection.path);
// Count documents and images // Count documents and images
@ -337,9 +254,9 @@ fn main() -> Result<()> {
if let Ok(files) = collection.file_list() { if let Ok(files) = collection.file_list() {
let image_count = files.iter() let image_count = files.iter()
.filter(|f| .filter(|f|
f.ends_with(".png") || f.ends_with(".jpg") || f.ends_with(".png") || f.ends_with(".jpg") ||
f.ends_with(".jpeg") || f.ends_with(".gif") || f.ends_with(".jpeg") || f.ends_with(".gif") ||
f.ends_with(".svg")) f.ends_with(".svg"))
.count(); .count();
println!(" Images: {}", image_count); println!(" Images: {}", image_count);
@ -349,6 +266,39 @@ fn main() -> Result<()> {
} }
} }
} }
} else if let Some(matches) = matches.subcommand_matches("delete-collection") {
let collection = matches.value_of("collection").unwrap();
let doctree_name = matches.value_of("doctree").unwrap_or("default");
// Create a storage with the specified doctree name
let storage = RedisStorage::new("redis://localhost:6379")?;
storage.set_doctree_name(doctree_name);
// Create a DocTree with the specified doctree name
let mut doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
println!("Deleting collection '{}' from Redis in doctree '{}'...", collection, doctree_name);
doctree.delete_collection(collection)?;
println!("Collection '{}' deleted successfully", collection);
} else if let Some(matches) = matches.subcommand_matches("reset") {
let doctree_name = matches.value_of("doctree").unwrap_or("default");
// Create a storage with the specified doctree name
let storage = RedisStorage::new("redis://localhost:6379")?;
storage.set_doctree_name(doctree_name);
// Create a DocTree with the specified doctree name
let mut doctree = DocTree::builder()
.with_storage(storage)
.with_doctree_name(doctree_name)
.build()?;
println!("Deleting all collections from Redis in doctree '{}'...", doctree_name);
doctree.delete_all_collections()?;
println!("All collections deleted successfully");
} else { } else {
println!("No command specified. Use --help for usage information."); println!("No command specified. Use --help for usage information.");
} }

View File

@ -7,7 +7,7 @@ set -e
cd doctreecmd cd doctreecmd
echo "=== Scanning Collections ===" echo "=== Scanning Collections ==="
cargo run -- scan-collections ../examples cargo run -- scan ../examples
echo -e "\n=== Listing Collections ===" echo -e "\n=== Listing Collections ==="
cargo run -- list cargo run -- list

View File

@ -8,32 +8,32 @@ cd doctreecmd
# First, scan the collections # First, scan the collections
echo "=== Scanning Collections ===" echo "=== Scanning Collections ==="
cargo run -- scan-and-info ../examples supercollection cargo run -- scan ../examples --doctree supercollection
# Get a document in markdown format # Get a document in markdown format
echo -e "\n=== Getting Document (Markdown) ===" echo -e "\n=== Getting Document (Markdown) ==="
cargo run -- get -c supercollection -p 01_features.md cargo run -- get -c supercollection -p 01_features.md --doctree supercollection
# Get a document in HTML format # Get a document in HTML format
echo -e "\n=== Getting Document (HTML) ===" echo -e "\n=== Getting Document (HTML) ==="
cargo run -- get -c supercollection -p 01_features.md -f html cargo run -- get -c supercollection -p 01_features.md -f html --doctree supercollection
# Get a document without specifying collection # Get a document without specifying collection
echo -e "\n=== Getting Document (Default Collection) ===" echo -e "\n=== Getting Document (Default Collection) ==="
cargo run -- get -p 01_features.md cargo run -- get -p 01_features.md --doctree supercollection
# Delete a specific collection # Delete a specific collection
echo -e "\n=== Deleting Collection ===" echo -e "\n=== Deleting Collection ==="
cargo run -- delete-collection grid_documentation cargo run -- delete-collection grid_documentation --doctree supercollection
# List remaining collections # List remaining collections
echo -e "\n=== Listing Remaining Collections ===" echo -e "\n=== Listing Remaining Collections ==="
cargo run -- list cargo run -- list --doctree supercollection
# Reset all collections # # Reset all collections
echo -e "\n=== Resetting All Collections ===" # echo -e "\n=== Resetting All Collections ==="
cargo run -- reset # cargo run -- reset --doctree supercollection
# Verify all collections are gone # # Verify all collections are gone
echo -e "\n=== Verifying Reset ===" # echo -e "\n=== Verifying Reset ==="
cargo run -- list # cargo run -- list --doctree supercollection