1#![allow(missing_docs)]
2
3use clap::{Parser, Subcommand};
4use std::path::PathBuf;
5
6#[derive(Debug, Parser)]
8#[command(name = "hexz", version, about = "High-performance, deduplicated data archives", long_about = None)]
9#[command(disable_help_flag = true)]
10#[command(styles = get_styles())]
11pub struct Cli {
12 #[arg(short, long, action = clap::ArgAction::SetTrue)]
14 pub help: bool,
15
16 #[command(subcommand)]
18 pub command: Option<Commands>,
19}
20
21fn get_styles() -> clap::builder::Styles {
22 use clap::builder::styling::{AnsiColor, Effects, Styles};
23 Styles::styled()
24 .header(AnsiColor::Yellow.on_default() | Effects::BOLD)
25 .usage(AnsiColor::Green.on_default() | Effects::BOLD)
26 .literal(AnsiColor::Cyan.on_default() | Effects::BOLD)
27 .placeholder(AnsiColor::Cyan.on_default())
28}
29
30#[derive(Subcommand, Debug, Clone)]
31pub enum RemoteCommand {
32 Add {
34 name: String,
36 url: String,
38 },
39 Remove {
41 name: String,
43 },
44 List,
46}
47
48#[derive(Debug, Subcommand)]
49pub enum Commands {
50 #[command(display_order = 1)]
55 #[command(
56 long_about = "Creates a compressed and deduplicated archive (.hxz). Uses Content-Defined Chunking (CDC) to identify shared blocks across versions."
57 )]
58 #[command(after_help = "hexz pack ./folder data.hxz --compression zstd")]
59 Pack {
60 input: PathBuf,
62
63 output: PathBuf,
65
66 #[arg(long, short)]
68 base: Option<PathBuf>,
69
70 #[arg(long, default_value = "lz4")]
72 compression: String,
73
74 #[arg(long)]
76 encrypt: bool,
77
78 #[arg(long, default_value_t = 65536, value_parser = clap::value_parser!(u32).range(1..))]
80 block_size: u32,
81
82 #[arg(long)]
84 workers: Option<usize>,
85
86 #[arg(long)]
88 dcam: bool,
89
90 #[arg(long)]
92 dcam_optimal: bool,
93
94 #[arg(long, short)]
96 silent: bool,
97 },
98
99 #[command(display_order = 2)]
101 #[command(after_help = "hexz extract data.hxz ./output")]
102 Extract {
103 input: PathBuf,
105
106 output: PathBuf,
108 },
109
110 #[command(display_order = 3)]
112 #[command(alias = "inspect")]
113 #[command(after_help = "hexz show ./data.hxz --json")]
114 Show {
115 snap: PathBuf,
117
118 #[arg(long)]
120 json: bool,
121 },
122
123 #[command(display_order = 4)]
125 #[command(after_help = "hexz diff v1.hxz v2.hxz")]
126 Diff {
127 a: PathBuf,
129
130 b: PathBuf,
132 },
133
134 #[command(display_order = 5)]
136 #[command(alias = "ls")]
137 Log {
138 dir: PathBuf,
140 },
141
142 #[command(display_order = 6)]
144 Convert {
145 format: String,
147
148 input: PathBuf,
150
151 output: PathBuf,
153
154 #[arg(long, default_value = "lz4")]
156 compression: String,
157
158 #[arg(long, default_value_t = 65536)]
160 block_size: u32,
161
162 #[arg(long, short)]
164 silent: bool,
165 },
166
167 #[command(display_order = 7)]
169 Predict {
170 path: PathBuf,
172
173 #[arg(long, default_value_t = 65536)]
175 block_size: u32,
176
177 #[arg(long)]
179 min_chunk: Option<u32>,
180
181 #[arg(long)]
183 avg_chunk: Option<u32>,
184
185 #[arg(long)]
187 max_chunk: Option<u32>,
188
189 #[arg(long)]
191 json: bool,
192 },
193
194 #[cfg(feature = "fuse")]
199 #[command(display_order = 10)]
200 #[command(
201 long_about = "Exposes the archive's content as a read-only filesystem. Only requested blocks are fetched/decompressed on-demand."
202 )]
203 #[command(after_help = "hexz mount data.hxz /mnt/data")]
204 Mount {
205 snap: String,
207
208 mountpoint: PathBuf,
210
211 #[arg(long, short)]
213 overlay: Option<PathBuf>,
214
215 #[arg(long, short = 'e')]
217 editable: bool,
218
219 #[arg(short, long)]
221 daemon: bool,
222
223 #[arg(long)]
225 cache_size: Option<String>,
226
227 #[arg(long, default_value_t = 0)]
229 uid: u32,
230
231 #[arg(long, default_value_t = 0)]
233 gid: u32,
234 },
235
236 #[cfg(feature = "fuse")]
238 #[command(display_order = 11)]
239 Unmount {
240 mountpoint: PathBuf,
242 },
243
244 #[cfg(feature = "fuse")]
246 #[command(display_order = 11)]
247 #[command(
248 long_about = "Mounts the archive to a temporary directory and drops you into a subshell. When the shell exits, the archive is automatically unmounted and the temporary directory is cleaned up."
249 )]
250 #[command(after_help = "hexz shell data.hxz --editable")]
251 Shell {
252 snap: String,
254
255 #[arg(long, short)]
257 overlay: Option<PathBuf>,
258
259 #[arg(long, short = 'e')]
261 editable: bool,
262
263 #[arg(long)]
265 cache_size: Option<String>,
266 },
267
268 #[cfg(feature = "fuse")]
270 #[command(display_order = 12)]
271 #[command(
272 long_about = "Takes a writable mount point and saves the modifications as a new thin archive linked to the original base."
273 )]
274 #[command(after_help = "hexz commit v2.hxz")]
275 Commit {
276 output: PathBuf,
278
279 mountpoint: Option<PathBuf>,
281
282 #[arg(long, short)]
284 base: Option<PathBuf>,
285 },
286
287 #[cfg(feature = "fuse")]
289 #[command(display_order = 13)]
290 #[command(alias = "co")]
291 Checkout {
292 archive: PathBuf,
294
295 path: PathBuf,
297 },
298
299 #[cfg(feature = "fuse")]
301 #[command(display_order = 14)]
302 #[command(alias = "st")]
303 Status {
304 path: Option<PathBuf>,
306 },
307
308 #[cfg(feature = "fuse")]
310 #[command(display_order = 15)]
311 Init {
312 path: Option<PathBuf>,
314 },
315
316 #[cfg(feature = "fuse")]
318 #[command(display_order = 16)]
319 Remote {
320 #[command(subcommand)]
321 action: RemoteCommand,
322 },
323
324 #[cfg(feature = "fuse")]
326 #[command(display_order = 17)]
327 Push {
328 #[arg(default_value = "origin")]
330 remote: String,
331
332 archive: Option<PathBuf>,
334 },
335
336 #[cfg(feature = "fuse")]
338 #[command(display_order = 18)]
339 Pull {
340 #[arg(default_value = "origin")]
342 remote: String,
343
344 archive: Option<String>,
346 },
347
348 #[cfg(feature = "server")]
353 #[command(display_order = 20)]
354 Serve {
355 snap: String,
357
358 #[arg(long, default_value_t = 8080)]
360 port: u16,
361
362 #[arg(long, default_value = "127.0.0.1")]
364 bind: String,
365
366 #[arg(short, long)]
368 daemon: bool,
369 },
370
371 #[cfg(feature = "signing")]
373 #[command(display_order = 21)]
374 Keygen {
375 #[arg(short, long)]
377 output_dir: Option<PathBuf>,
378 },
379
380 #[cfg(feature = "signing")]
382 #[command(display_order = 22)]
383 Sign {
384 key: PathBuf,
386
387 image: PathBuf,
389 },
390
391 #[cfg(feature = "signing")]
393 #[command(display_order = 23)]
394 Verify {
395 key: PathBuf,
397
398 image: PathBuf,
400 },
401
402 #[command(display_order = 24)]
404 Doctor,
405}