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