forked from
atscan.net/plcbundle-rs
High-performance implementation of plcbundle written in Rust
1use anyhow::Result;
2use clap::{Parser, Subcommand, ValueHint};
3use plcbundle::*;
4use std::path::PathBuf;
5
6#[cfg(feature = "dhat-heap")]
7#[global_allocator]
8static ALLOC: dhat::Alloc = dhat::Alloc;
9
10// CLI Commands (cmd_ prefix)
11mod cmd_bench;
12mod cmd_clean;
13mod cmd_clone;
14mod cmd_compare;
15mod cmd_completions;
16mod cmd_did;
17mod cmd_export;
18mod cmd_index;
19mod cmd_init;
20mod cmd_inspect;
21mod cmd_ls;
22mod cmd_mempool;
23mod cmd_migrate;
24mod cmd_op;
25mod cmd_query;
26mod cmd_random;
27mod cmd_rebuild;
28mod cmd_rollback;
29mod cmd_server;
30mod cmd_stats;
31mod cmd_status;
32mod cmd_sync;
33mod cmd_verify;
34
35// Helper modules (no cmd_ prefix)
36mod logger;
37mod progress;
38mod utils;
39
40const VERSION: &str = env!("CARGO_PKG_VERSION");
41
42/// Format custom help template with grouped commands
43fn format_help_template() -> &'static str {
44 concat!(
45 "{about-with-newline}\n\n",
46 "{usage-heading}\n {usage}\n\n",
47 "Options:\n{options}\n\n",
48 "Repository Management:\n",
49 " init Initialize a new bundle repository\n",
50 " clone Clone a remote bundle repository\n",
51 " sync Fetch new bundles from PLC directory\n",
52 " status Show comprehensive repository status\n",
53 " ls List bundles (machine-readable)\n",
54 "\n",
55 "Maintenance:\n",
56 " rebuild Rebuild bundle index from existing files\n",
57 " migrate Migrate bundles to new bundle format\n",
58 " clean Remove all temporary files from the repository\n",
59 " rollback Rollback repository to earlier state\n",
60 "\n",
61 "Query & Export:\n",
62 " query, q Query bundles with JMESPath or simple path\n",
63 " export Export operations to stdout or file\n",
64 " op Operation queries and inspection\n",
65 " stats Display statistics about bundles\n",
66 "\n",
67 "DID Operations:\n",
68 " did DID operations and queries\n",
69 " handle Resolve handle to DID\n",
70 " index DID index management\n",
71 "\n",
72 "Verification:\n",
73 " verify Verify bundle integrity and chain\n",
74 " compare Compare bundle repositories\n",
75 " inspect Deep analysis of bundle contents\n",
76 "\n",
77 "Server:\n",
78 " server Start HTTP server\n",
79 "\n",
80 "Mempool:\n",
81 " mempool Manage mempool operations\n",
82 "\n",
83 "Development:\n",
84 " bench Benchmark bundle operations\n",
85 " random Output random DIDs sampled from the index\n",
86 "\n",
87 "Utilities:\n",
88 " completions Generate shell completion scripts\n",
89 "\n",
90 "See 'plcbundle <COMMAND> --help' for more information on a specific command.\n"
91 )
92}
93
94#[derive(Parser)]
95#[command(bin_name = "plcbundle")]
96#[command(version = VERSION)]
97#[command(about = concat!("plcbundle v", env!("CARGO_PKG_VERSION"), " (rust) - DID PLC Bundle Management"))]
98#[command(long_about = concat!(
99 "plcbundle v", env!("CARGO_PKG_VERSION"), " - DID PLC Bundle Management\n\n",
100 "Tool for archiving AT Protocol's DID PLC Directory operations\n",
101 "into immutable, cryptographically-chained bundles of 10,000\n",
102 "operations each.\n\n",
103 "Documentation: https://tangled.org/@atscan.net/plcbundle"
104))]
105#[command(author)]
106#[command(propagate_version = true)]
107#[command(help_template = format_help_template())]
108pub struct Cli {
109 /// Repository directory
110 #[arg(short = 'C', long = "dir", global = true, default_value = ".", value_hint = ValueHint::DirPath)]
111 dir: PathBuf,
112
113 /// Suppress progress output
114 #[arg(long, global = true)]
115 quiet: bool,
116
117 /// Enable verbose output
118 #[arg(short = 'v', long, global = true)]
119 verbose: bool,
120
121 #[command(subcommand)]
122 command: Commands,
123}
124
125#[derive(Subcommand)]
126enum Commands {
127 Init(cmd_init::InitCommand),
128 Clone(cmd_clone::CloneCommand),
129 Sync(cmd_sync::SyncCommand),
130 Status(cmd_status::StatusCommand),
131 Ls(cmd_ls::LsCommand),
132 Rebuild(cmd_rebuild::RebuildCommand),
133 Migrate(cmd_migrate::MigrateCommand),
134 Clean(cmd_clean::CleanCommand),
135 Rollback(cmd_rollback::RollbackCommand),
136 Query(cmd_query::QueryCommand),
137 Export(cmd_export::ExportCommand),
138 Op(cmd_op::OpCommand),
139 Stats(cmd_stats::StatsCommand),
140 Did(cmd_did::DidCommand),
141 Handle(cmd_did::HandleCommand),
142 Index(cmd_index::IndexCommand),
143 Verify(cmd_verify::VerifyCommand),
144 Compare(cmd_compare::CompareCommand),
145 Inspect(cmd_inspect::InspectCommand),
146 Server(cmd_server::ServerCommand),
147 Mempool(cmd_mempool::MempoolCommand),
148 Bench(cmd_bench::BenchCommand),
149 Random(cmd_random::RandomCommand),
150 Completions(cmd_completions::CompletionsCommand),
151}
152
153fn main() -> Result<()> {
154 #[cfg(feature = "dhat-heap")]
155 let _profiler = dhat::Profiler::new_heap();
156
157 let cli = Cli::parse();
158
159 // Initialize logger based on verbosity flags
160 logger::init_logger(cli.verbose, cli.quiet);
161
162 match cli.command {
163 Commands::Query(cmd) => cmd_query::run(cmd, cli.dir, cli.quiet, cli.verbose)?,
164 Commands::Init(cmd) => cmd_init::run(cmd)?,
165 Commands::Clone(cmd) => cmd_clone::run(cmd)?,
166 Commands::Status(cmd) => cmd_status::run(cmd, cli.dir)?,
167 Commands::Ls(cmd) => cmd_ls::run(cmd, cli.dir, cli.verbose, cli.quiet)?,
168 Commands::Verify(cmd) => cmd_verify::run(cmd, cli.dir, cli.verbose)?,
169 Commands::Export(cmd) => cmd_export::run(cmd, cli.dir, cli.quiet, cli.verbose)?,
170 Commands::Op(cmd) => cmd_op::run(cmd, cli.dir, cli.quiet)?,
171 Commands::Stats(cmd) => cmd_stats::run(cmd, cli.dir)?,
172 Commands::Handle(cmd) => cmd_did::run_handle(cmd, cli.dir)?,
173 Commands::Did(cmd) => cmd_did::run_did(cmd, cli.dir, cli.verbose)?,
174 Commands::Index(cmd) => cmd_index::run(cmd, cli.dir, cli.verbose)?,
175 Commands::Mempool(cmd) => cmd_mempool::run(cmd, cli.dir, cli.verbose)?,
176 Commands::Sync(cmd) => cmd_sync::run(cmd, cli.dir, cli.quiet, cli.verbose)?,
177 Commands::Rollback(cmd) => cmd_rollback::run(cmd, cli.dir, cli.verbose)?,
178 Commands::Compare(cmd) => cmd_compare::run(cmd, cli.dir, cli.verbose)?,
179 Commands::Inspect(cmd) => cmd_inspect::run(cmd, cli.dir)?,
180 Commands::Server(cmd) => cmd_server::run(cmd, cli.dir, cli.verbose)?,
181 Commands::Migrate(cmd) => cmd_migrate::run(cmd, cli.dir, cli.verbose)?,
182 Commands::Rebuild(cmd) => cmd_rebuild::run(cmd, cli.dir, cli.verbose)?,
183 Commands::Bench(cmd) => cmd_bench::run(cmd, cli.dir, cli.verbose)?,
184 Commands::Random(cmd) => cmd_random::run(cmd, cli.dir)?,
185 Commands::Clean(cmd) => cmd_clean::run(cmd, cli.dir, cli.verbose)?,
186 Commands::Completions(cmd) => cmd_completions::run(cmd)?,
187 }
188
189 Ok(())
190}
191
192/// Macro to create clap help templates with examples
193/// This works around the limitation that {bin} doesn't work in after_help
194/// Uses env! macro to get binary name at compile time
195#[macro_export]
196macro_rules! clap_help {
197 (examples: $examples:literal) => {{
198 const BIN: &str = env!("CARGO_PKG_NAME");
199 concat!(
200 "{about-with-newline}\n",
201 "{usage-heading} {usage}\n\n",
202 "{all-args}\n\n",
203 "Examples:\n",
204 $examples
205 )
206 .replace("{bin}", BIN)
207 }};
208
209 (before: $before:literal, examples: $examples:literal) => {{
210 const BIN: &str = env!("CARGO_PKG_NAME");
211 concat!(
212 "{about-with-newline}\n",
213 $before,
214 "\n\n",
215 "{usage-heading} {usage}\n\n",
216 "{all-args}\n\n",
217 "Examples:\n",
218 $examples
219 )
220 .replace("{bin}", BIN)
221 }};
222}