High-performance implementation of plcbundle written in Rust
at main 130 lines 4.4 kB view raw
1// Rebuild plc_bundles.json from existing bundle files 2use super::progress::ProgressBar; 3use super::utils::{HasGlobalFlags, format_bytes}; 4use anyhow::Result; 5use clap::{Args, ValueHint}; 6use plcbundle::BundleManager; 7use std::path::PathBuf; 8use std::time::Instant; 9 10#[derive(Args)] 11#[command( 12 about = "Rebuild bundle index from existing files", 13 long_about = "Reconstruct the bundle index (plc_bundles.json) by scanning existing bundle 14files and extracting metadata from their embedded metadata frames. This is essential 15when the index file has been lost, corrupted, or needs to be regenerated. 16 17The command scans all .jsonl.zst files in the directory, reads the metadata frame 18from each bundle (stored in a zstd skippable frame), and reconstructs the complete 19index with proper chain hashes and bundle relationships. The index is written 20atomically to ensure data integrity. 21 22This only works with bundles that have embedded metadata frames (the modern format). 23Legacy bundles without metadata frames cannot be reconstructed this way. Use 24--dry-run to preview what would be rebuilt without actually writing the index. 25 26After rebuilding, verify the repository with 'verify' to ensure chain integrity 27is correct.", 28 help_template = crate::clap_help!( 29 examples: " # Rebuild index from current directory\n \ 30 {bin} rebuild\n\n \ 31 # Rebuild from specific directory\n \ 32 {bin} rebuild -C /path/to/bundles\n\n \ 33 # Show verbose output\n \ 34 {bin} rebuild -v\n\n \ 35 # Dry-run (just scan, don't write)\n \ 36 {bin} rebuild --dry-run" 37 ) 38)] 39pub struct RebuildCommand { 40 /// Show what would be done without writing index 41 #[arg(short = 'n', long)] 42 pub dry_run: bool, 43 44 /// Set origin URL (default: auto-detect from first bundle) 45 #[arg(long, value_hint = ValueHint::Url)] 46 pub origin: Option<String>, 47} 48 49impl HasGlobalFlags for RebuildCommand { 50 fn verbose(&self) -> bool { 51 false 52 } 53 fn quiet(&self) -> bool { 54 false 55 } 56} 57 58pub fn run(cmd: RebuildCommand, dir: PathBuf, _global_verbose: bool) -> Result<()> { 59 eprintln!( 60 "Rebuilding bundle index from: {}\n", 61 super::utils::display_path(&dir).display() 62 ); 63 64 let start = Instant::now(); 65 66 // Progress tracking 67 eprintln!("Scanning for bundle files..."); 68 69 // We don't know the total upfront, so we'll create progress bar in the callback 70 use std::sync::{Arc, Mutex}; 71 let progress_bar: Arc<Mutex<Option<ProgressBar>>> = Arc::new(Mutex::new(None)); 72 let progress_bar_clone = progress_bar.clone(); 73 74 // Rebuild index using BundleManager API 75 let index = BundleManager::rebuild_index( 76 &dir, 77 cmd.origin, 78 Some(move |current, total, bytes_processed, total_bytes| { 79 let mut pb_guard = progress_bar_clone.lock().unwrap(); 80 if pb_guard.is_none() { 81 *pb_guard = Some(ProgressBar::with_bytes(total, total_bytes)); 82 } 83 if let Some(ref pb) = *pb_guard { 84 pb.set_with_bytes(current, bytes_processed); 85 } 86 }), 87 )?; 88 89 if let Some(pb) = progress_bar.lock().unwrap().take() { 90 pb.finish(); 91 } 92 93 let elapsed = start.elapsed(); 94 95 // Display summary 96 eprintln!(); 97 eprintln!("Rebuild Summary"); 98 eprintln!("═══════════════"); 99 eprintln!(" Bundles: {}", index.bundles.len()); 100 eprintln!( 101 " Range: {} - {}", 102 index.bundles.first().map(|b| b.bundle_number).unwrap_or(0), 103 index.last_bundle 104 ); 105 eprintln!(" Origin: {}", index.origin); 106 eprintln!( 107 " Compressed size: {}", 108 format_bytes(index.total_size_bytes) 109 ); 110 eprintln!( 111 " Uncompressed size: {}", 112 format_bytes(index.total_uncompressed_size_bytes) 113 ); 114 eprintln!(" Scan time: {:?}", elapsed); 115 eprintln!(); 116 117 if cmd.dry_run { 118 eprintln!("💡 Dry-run mode - index not written"); 119 return Ok(()); 120 } 121 122 // Save index atomically 123 eprintln!("Writing plc_bundles.json..."); 124 index.save(&dir)?; 125 126 eprintln!("✓ Index rebuilt successfully"); 127 eprintln!(" Location: {}/plc_bundles.json", dir.display()); 128 129 Ok(()) 130}