use clap::{App, Arg, SubCommand}; use doctree::{DocTree, RedisStorage, Result, from_directory}; use std::path::Path; fn main() -> Result<()> { let matches = App::new("doctree") .version("0.1.0") .author("Your Name") .about("A tool to manage document collections") .arg( Arg::with_name("debug") .long("debug") .help("Enable debug logging") .takes_value(false) ) .subcommand( SubCommand::with_name("scan") .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("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")), ) .subcommand( SubCommand::with_name("list") .about("List collections") .arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")), ) .subcommand( SubCommand::with_name("info") .about("Show detailed information about collections") .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::with_name("get") .about("Get page content") .arg(Arg::with_name("collection") .short("c".chars().next().unwrap()) .long("collection") .takes_value(true) .help("Name of the collection (optional)")) .arg(Arg::with_name("page") .short("p".chars().next().unwrap()) .long("page") .required(true) .takes_value(true) .help("Name of the page")) .arg(Arg::with_name("format") .short("f".chars().next().unwrap()) .long("format") .takes_value(true) .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::with_name("html") .about("Get page content as HTML") .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("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")), ) .subcommand( SubCommand::with_name("delete") .about("Delete a collection from Redis") .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::with_name("reset") .about("Delete all collections from Redis") .arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")), ) .subcommand( SubCommand::with_name("export_to_ipfs") .about("Export a collection to IPFS") .arg(Arg::with_name("collection").required(true).help("Name of the collection")) .arg(Arg::with_name("output").required(true).help("Output directory for IPFS export")) .arg(Arg::with_name("doctree").long("doctree").takes_value(true).help("Name of the doctree (default: 'default')")), ) .get_matches(); // Check if debug mode is enabled let debug_mode = matches.is_present("debug"); // Handle subcommands if let Some(matches) = matches.subcommand_matches("scan") { let path = matches.value_of("path").unwrap(); if debug_mode { println!("DEBUG: Scanning path: {}", path); } let doctree_name = matches.value_of("doctree").unwrap_or("default"); 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 let doctree = from_directory(Path::new(path), Some(doctree_name))?; // Print the discovered collections let collections = doctree.list_collections(); if collections.is_empty() { println!("No collections found"); } else { println!("Discovered collections:"); for collection in collections { println!("- {}", collection); } } } else if let Some(matches) = matches.subcommand_matches("list") { let doctree_name = matches.value_of("doctree").unwrap_or("default"); if debug_mode { println!("DEBUG: Listing collections for doctree: {}", doctree_name); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // Get collections directly from Redis to avoid debug output from DocTree let collections = storage.list_all_collections()?; if collections.is_empty() { println!("No collections found in doctree '{}'", doctree_name); } 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"); if debug_mode { println!("DEBUG: Getting page '{}' from collection '{}' in doctree '{}' with format '{}'", page, collection.unwrap_or("(default)"), doctree_name, format); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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"); if debug_mode { println!("DEBUG: Getting HTML for page '{}' from collection '{}' in doctree '{}'", page, collection, doctree_name); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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"); let collection_name = matches.value_of("collection"); if debug_mode { if let Some(name) = collection_name { println!("DEBUG: Getting info for collection '{}' in doctree '{}'", name, doctree_name); } else { println!("DEBUG: Getting info for all collections in doctree '{}'", doctree_name); } } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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 { // 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:{}", doctree_name, 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, doctree_name, 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 = 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, doctree_name, collection.name, image); } // Filter other files let other_files: Vec = 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, doctree_name, collection.name, file); } }, Err(e) => println!(" Error listing files: {}", e), } }, Err(e) => println!("Error: {}", e), } } else { // Show info for all collections let collections = doctree.list_collections(); if collections.is_empty() { println!("No collections found"); } else { println!("Collections in doctree '{}':", doctree_name); for name in collections { if let Ok(collection) = doctree.get_collection(&name) { println!("- {} (Redis Key: {}:collections:{})", name, doctree_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("delete") { let collection = matches.value_of("collection").unwrap(); let doctree_name = matches.value_of("doctree").unwrap_or("default"); if debug_mode { println!("DEBUG: Deleting collection '{}' from doctree '{}'", collection, doctree_name); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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("export_to_ipfs") { let collection_name = matches.value_of("collection").unwrap(); let output_path = matches.value_of("output").unwrap(); let doctree_name = matches.value_of("doctree").unwrap_or("default"); if debug_mode { println!("DEBUG: Exporting collection '{}' from doctree '{}' to IPFS output path '{}'", collection_name, doctree_name, output_path); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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()?; // Get the collection let collection = doctree.get_collection(collection_name)?; // Call the synchronous export_collection_to_ipfs_sync function from the doctree crate let output_path = Path::new(output_path); doctree.export_collection_to_ipfs(collection_name, output_path)?; println!("Successfully exported collection '{}' to IPFS and generated metadata CSV at {:?}.", collection_name, output_path.join(format!("{}.csv", collection_name))); } else if let Some(matches) = matches.subcommand_matches("reset") { let doctree_name = matches.value_of("doctree").unwrap_or("default"); if debug_mode { println!("DEBUG: Resetting all collections in doctree '{}'", doctree_name); } // Create a storage with the specified doctree name let storage = RedisStorage::new("redis://localhost:6379")?; storage.set_doctree_name(doctree_name); storage.set_debug(debug_mode); if debug_mode { println!("DEBUG: Connected to Redis storage"); } // 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 { println!("No command specified. Use --help for usage information."); } Ok(()) }