use clap::{Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(
name = "fdock",
version,
about = "Ferrodock on the command line: containers, images, logs and Quick start \
templates, on Ferrodock's own engine or any other Docker engine."
)]
pub struct Cli {
#[arg(long, global = true)]
pub json: bool,
#[arg(long, global = true, value_name = "NAME")]
pub engine: Option<String>,
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub enum Command {
Ps {
#[arg(short, long)]
all: bool,
},
Start {
#[arg(required = true)]
containers: Vec<String>,
},
Stop {
#[arg(required = true)]
containers: Vec<String>,
},
Restart {
#[arg(required = true)]
containers: Vec<String>,
},
Pause {
#[arg(required = true)]
containers: Vec<String>,
},
Unpause {
#[arg(required = true)]
containers: Vec<String>,
},
Rm {
#[arg(required = true)]
containers: Vec<String>,
#[arg(short, long)]
force: bool,
},
Logs {
container: String,
#[arg(short = 'n', long, default_value_t = 100)]
tail: usize,
#[arg(short, long)]
follow: bool,
#[arg(short, long)]
timestamps: bool,
},
Inspect {
container: String,
#[arg(long)]
show_secrets: bool,
#[arg(long)]
raw: bool,
},
Images,
Pull { image: String },
Volumes,
Networks,
Projects,
Df,
Quickstart {
template: Option<String>,
#[arg(long)]
name: Option<String>,
#[arg(long = "version", value_name = "TAG")]
tag: Option<String>,
#[arg(long = "port", value_name = "PORT")]
ports: Vec<u16>,
#[arg(long = "set", value_name = "KEY=VALUE", value_parser = key_value)]
values: Vec<(String, String)>,
#[arg(long)]
no_data: bool,
},
Engine,
}
fn key_value(text: &str) -> Result<(String, String), String> {
text.split_once('=')
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.ok_or_else(|| format!("expected KEY=VALUE, got {text}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_like_the_docker_command() {
let cli = Cli::parse_from(["fdock", "ps", "-a", "--json"]);
assert!(cli.json);
assert!(matches!(cli.command, Command::Ps { all: true }));
let cli = Cli::parse_from(["fdock", "--engine", "work", "logs", "web", "-n", "5", "-f"]);
assert_eq!(cli.engine.as_deref(), Some("work"));
assert!(matches!(
cli.command,
Command::Logs {
tail: 5,
follow: true,
..
}
));
let cli = Cli::parse_from([
"fdock",
"quickstart",
"postgres",
"--port",
"6543",
"--set",
"POSTGRES_PASSWORD=x=y",
]);
let Command::Quickstart { ports, values, .. } = cli.command else {
panic!()
};
assert_eq!(ports, [6543]);
assert_eq!(values, [("POSTGRES_PASSWORD".to_owned(), "x=y".to_owned())]);
assert!(Cli::try_parse_from(["fdock", "stop"]).is_err());
Cli::command().debug_assert();
}
use clap::CommandFactory;
}