podbox/cli/command.rs
1//! The `podbox` command tree: the `Command` subcommand enum and its
2//! nested `Export`/`Profile`/`Snapshot` command enums.
3//!
4//! Extracted verbatim from `cli.rs`; re-exported from `cli` so
5//! `podbox::cli::Command` paths keep resolving.
6
7use clap::Subcommand;
8
9use super::{OutputFormat, Shell};
10
11#[derive(Subcommand)]
12pub enum Command {
13 /// Internal stdin watchdog for interactive sessions. Not for direct use.
14 #[command(hide = true)]
15 InternalStdinWatchdog {
16 /// PID of the process to terminate when stdin hangs up.
17 parent_pid: u32,
18 },
19
20 /// Build the container image from the definition.
21 #[command(display_order = 31)]
22 Build {
23 /// Container name to build (overrides auto-detection).
24 name: Option<String>,
25 /// Force rebuild even if definition hasn't changed.
26 #[arg(long)]
27 rebuild: bool,
28 /// Skip post-build drift check.
29 #[arg(long)]
30 no_diff: bool,
31 /// Open config in editor before building.
32 #[arg(long)]
33 edit: bool,
34 },
35
36 /// Install Quadlet systemd files and enable the container.
37 #[command(display_order = 32)]
38 Enable {
39 /// Container name (overrides auto-detection / active context).
40 name: Option<String>,
41 /// Skip confirmation when reinstalling Quadlet on a running container.
42 #[arg(long)]
43 yes: bool,
44 },
45
46 /// Disable and remove Quadlet systemd files.
47 #[command(display_order = 33)]
48 Disable {
49 /// Container name (overrides auto-detection / active context).
50 name: Option<String>,
51 /// Skip config loading and remove Quadlet files by name only.
52 #[arg(long)]
53 force: bool,
54 },
55
56 /// Start the container.
57 #[command(display_order = 23)]
58 Start {
59 /// Container name (overrides auto-detection / active context).
60 name: Option<String>,
61 /// Maximum seconds to wait for the container to become ready.
62 #[arg(long, default_value = "30")]
63 timeout: u64,
64 /// Open config in editor before starting.
65 #[arg(long)]
66 edit: bool,
67 },
68
69 /// Stop the container.
70 #[command(display_order = 24)]
71 Stop {
72 /// Container name (overrides auto-detection / active context).
73 name: Option<String>,
74 },
75
76 /// Execute a command interactively in the container.
77 #[command(display_order = 21)]
78 Exec {
79 /// Run as root inside the container (omit -u flag).
80 #[arg(long)]
81 root: bool,
82 /// Command and arguments to execute.
83 #[arg(required = true, trailing_var_arg = true)]
84 args: Vec<String>,
85 },
86
87 /// Run a GUI application in the container (detached).
88 #[command(display_order = 22)]
89 Run {
90 /// Application to run.
91 app: String,
92 /// Additional arguments for the application.
93 #[arg(trailing_var_arg = true)]
94 app_args: Vec<String>,
95 },
96
97 /// Show container status.
98 #[command(display_order = 26)]
99 Status {
100 /// Container name (overrides auto-detection / active context).
101 name: Option<String>,
102 /// Output format (text or json).
103 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
104 output: OutputFormat,
105 },
106
107 /// Show container logs.
108 #[command(display_order = 40)]
109 Logs {
110 /// Container name (overrides auto-detection / active context).
111 name: Option<String>,
112 /// Follow log output.
113 #[arg(short, long)]
114 follow: bool,
115 /// Number of lines to show from the end (default: 50).
116 #[arg(short, long)]
117 tail: Option<u32>,
118 /// Show logs since this duration (e.g. "5m", "1h", "2024-01-01").
119 #[arg(long)]
120 since: Option<String>,
121 },
122
123 /// Export a .desktop app or binary shim to the host.
124 #[command(display_order = 53)]
125 Export {
126 #[command(subcommand)]
127 export_cmd: ExportCommand,
128 },
129
130 /// Show resource usage for the container (wraps podman stats).
131 #[command(display_order = 42)]
132 Stats {
133 /// Container name (overrides auto-detection / active context).
134 name: Option<String>,
135 /// Only show one snapshot, don't stream.
136 #[arg(long)]
137 no_stream: bool,
138 /// Output format (text or json).
139 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
140 output: OutputFormat,
141 },
142
143 /// Remove the container.
144 #[command(visible_alias = "rm", display_order = 60)]
145 Remove {
146 /// Container name (overrides auto-detection / active context).
147 name: Option<String>,
148 /// Also remove the home directory.
149 #[arg(long)]
150 all: bool,
151 /// Skip confirmation prompt.
152 #[arg(long)]
153 force: bool,
154 /// Remove stale/orphaned containers (no valid config, not running).
155 #[arg(long)]
156 stale: bool,
157 /// Also delete the TOML definition file.
158 #[arg(long)]
159 config: bool,
160 },
161
162 /// Inspect container configuration, generated Quadlet, or computed environment.
163 #[command(display_order = 41)]
164 Inspect {
165 /// Container name (overrides auto-detection / active context).
166 name: Option<String>,
167 /// Show the resolved TOML config.
168 #[arg(long)]
169 config: bool,
170 /// Show the generated Quadlet (.container file).
171 #[arg(long)]
172 quadlet: bool,
173 /// Show the computed environment variables.
174 #[arg(long)]
175 env: bool,
176 /// Output format (text or json).
177 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
178 output: OutputFormat,
179 },
180
181 /// Run the host socket server (socket-activated by systemd).
182 #[command(hide = true)]
183 Serve {
184 /// Container name to serve.
185 name: String,
186 },
187
188 /// Run the Wayland firewall proxy (systemd companion service).
189 #[command(hide = true)]
190 Compositor {
191 /// Container name to proxy.
192 name: String,
193 },
194
195 /// Open an interactive shell in the container.
196 #[command(visible_alias = "shell", display_order = 20)]
197 Enter {
198 /// Container name (overrides auto-detection / active context).
199 name: Option<String>,
200 /// Open config in editor before entering shell.
201 #[arg(long)]
202 edit: bool,
203 },
204
205 /// Create and start a container from a profile or image in one step.
206 #[command(display_order = 10)]
207 Create {
208 /// Profile name (fedora, cachy) or full image reference.
209 image: String,
210 /// Override the container name.
211 #[arg(long, short)]
212 name: Option<String>,
213 /// Comma-separated list of packages to install (e.g. "fastfetch,btop").
214 #[arg(long, short)]
215 packages: Option<String>,
216 /// Skip starting the container after setup.
217 #[arg(long)]
218 no_start: bool,
219 /// Open config in editor before creating.
220 #[arg(long)]
221 edit: bool,
222 },
223
224 /// Open the container config in your preferred editor.
225 #[command(display_order = 30)]
226 Edit {
227 /// Container name (overrides auto-detection / active context).
228 name: Option<String>,
229 /// After saving, rebuild the image if image config changed.
230 #[arg(long)]
231 rebuild: bool,
232 },
233
234 /// List all managed containers.
235 #[command(visible_alias = "ls", display_order = 25)]
236 List {
237 /// Output format (text or json).
238 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
239 output: OutputFormat,
240 },
241
242 /// Clone an existing container config to a new name.
243 #[command(display_order = 50)]
244 Clone {
245 /// Source container name.
246 src: String,
247 /// Destination container name.
248 dst: String,
249 /// Also copy the home directory contents.
250 #[arg(long)]
251 copy_home: bool,
252 },
253
254 /// Initialize a new container config.
255 #[command(display_order = 11)]
256 Init {
257 /// Base image reference (e.g. "fedora:44") for a non-prebuilt container.
258 /// If omitted, defaults to "fedora:44".
259 image: Option<String>,
260 /// Container name (defaults to the image name).
261 #[arg(long)]
262 name: Option<String>,
263 /// Run an interactive wizard to build the config.
264 #[arg(long, short = 'i', conflicts_with = "profile")]
265 interactive: bool,
266 /// Use a named profile (cachy, fedora, dev) as template.
267 #[arg(long)]
268 profile: Option<String>,
269 },
270
271 /// Pull the latest image and restart the container.
272 #[command(display_order = 34)]
273 Update {
274 /// Container name (overrides auto-detection / active context).
275 name: Option<String>,
276 /// Skip restart after update.
277 #[arg(long)]
278 no_restart: bool,
279 },
280
281 /// Pull a prebuilt image without building.
282 #[command(display_order = 35)]
283 Pull {
284 /// Distro shorthand or full image reference.
285 image: Option<String>,
286 },
287
288 /// Manage container profiles.
289 #[command(display_order = 12)]
290 Profile {
291 #[command(subcommand)]
292 profile_cmd: ProfileCommand,
293 },
294
295 /// Run diagnostic checks.
296 #[command(display_order = 43)]
297 Doctor {
298 /// Container name (overrides auto-detection / active context).
299 name: Option<String>,
300 /// Auto-fix common issues (e.g. corrupted Wayland socket ownership).
301 #[arg(long)]
302 fix: bool,
303 /// Output format (text or json).
304 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
305 output: OutputFormat,
306 },
307
308 /// Generate shell completions.
309 #[command(display_order = 80)]
310 Completions {
311 /// Shell to generate completions for.
312 shell: Shell,
313 /// Also print daily-driver `abbr` shorthand (fish only).
314 #[arg(long, action = clap::ArgAction::SetTrue)]
315 abbrs: bool,
316 },
317
318 /// Compare declared packages against the running container.
319 #[command(display_order = 36)]
320 Diff {
321 /// Container name (overrides auto-detection / active context).
322 name: Option<String>,
323 /// Update the config TOML's install list to match the container.
324 #[arg(long)]
325 apply: bool,
326 /// Output format (text or json).
327 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
328 output: OutputFormat,
329 },
330
331 /// Snapshot the current container state as a tagged image.
332 #[command(display_order = 51)]
333 Snapshot {
334 #[command(subcommand)]
335 snapshot_cmd: SnapshotCommand,
336 },
337
338 /// Restore a container from a snapshot.
339 #[command(display_order = 52)]
340 Restore {
341 /// Tag of the snapshot to restore.
342 tag: String,
343 /// Container name (overrides auto-detection / active context).
344 name: Option<String>,
345 },
346
347 /// Set or show active context.
348 #[command(display_order = 70)]
349 Use {
350 /// Container name to set as active (omit to show current context).
351 name: Option<String>,
352 /// Clear the active context.
353 #[arg(long)]
354 clear: bool,
355 },
356
357 /// Find the definition file that would be used.
358 #[command(display_order = 44)]
359 FindDefinition {
360 /// Container name (overrides auto-detection / active context).
361 name: Option<String>,
362 },
363
364 /// Guided repair for a container that won't start.
365 #[command(display_order = 45)]
366 Recover {
367 /// Container name (overrides auto-detection / active context).
368 name: Option<String>,
369 /// Run every step without prompting.
370 #[arg(long)]
371 yes: bool,
372 },
373
374 /// Show the recent lifecycle action history.
375 #[command(display_order = 46)]
376 History {
377 /// Container name filter (leave empty for all containers).
378 name: Option<String>,
379 /// Maximum number of entries to show (0 = no limit).
380 #[arg(long, default_value_t = 25)]
381 limit: usize,
382 /// Output format (text or json).
383 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
384 output: OutputFormat,
385 },
386
387 /// Translate a path between host and container.
388 #[command(display_order = 81, group(
389 clap::ArgGroup::new("direction")
390 .args(["to_container", "to_host"])
391 .required(true)
392 .multiple(false)
393 ))]
394 TranslatePath {
395 /// Direction of translation.
396 #[arg(long)]
397 to_container: bool,
398 /// Direction of translation.
399 #[arg(long)]
400 to_host: bool,
401 /// Path to translate.
402 path: String,
403 },
404
405 /// Migrate legacy config files from ~/.config/podbox/ to ~/.config/podbox/profiles/
406 #[command(display_order = 75)]
407 Migrate {
408 /// Overwrite files in target profiles/ directory if they exist.
409 #[arg(long)]
410 force: bool,
411 },
412
413 /// Print known container names, one per line (shell completion helper).
414 #[command(hide = true, name = "__complete-names")]
415 CompleteNames,
416}
417
418#[derive(Subcommand)]
419pub enum ExportCommand {
420 /// Export a .desktop application.
421 App {
422 /// Application name to export (omit with --all).
423 name: Option<String>,
424 /// Export all apps listed in the config.
425 #[arg(long, conflicts_with = "name")]
426 all: bool,
427 },
428 /// Export a binary shim.
429 Bin {
430 /// Binary name to export (omit with --all).
431 name: Option<String>,
432 /// Export all bins listed in the config.
433 #[arg(long, conflicts_with = "name")]
434 all: bool,
435 },
436 /// Remove all exports for the container.
437 Clean,
438 /// List apps and bins exported to the host for the container.
439 List,
440}
441
442#[derive(Subcommand)]
443pub enum ProfileCommand {
444 /// List all available profiles (built-in and custom).
445 List,
446 /// Show the configuration of a specific profile.
447 Show {
448 /// Name of the profile to display.
449 name: String,
450 },
451}
452
453#[derive(Debug, Clone, Subcommand)]
454pub enum SnapshotCommand {
455 /// Take a snapshot of the current container state.
456 Create {
457 /// Container name (overrides auto-detection / active context).
458 name: Option<String>,
459 /// Snapshot tag (defaults to timestamp).
460 #[arg(long, short)]
461 tag: Option<String>,
462 },
463 /// List snapshots for a container.
464 List {
465 /// Container name (overrides auto-detection / active context).
466 name: Option<String>,
467 /// Output format (text or json).
468 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
469 output: OutputFormat,
470 },
471 /// Prune old snapshots, keeping the newest N.
472 Prune {
473 /// Container name (overrides auto-detection / active context).
474 name: Option<String>,
475 /// Number of snapshots to keep (default: 5).
476 #[arg(long, default_value_t = 5)]
477 keep: usize,
478 },
479}