//! The whole command surface, in one file, so the shape of the CLI reads at a //! glance. Nothing here does any work — every variant is dispatched in `main`. use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; #[derive(Debug, Parser)] #[command( name = "spectral", version, about = "A monolithic kernel work wrapper to make it easy.", subcommand_required = true, arg_required_else_help = true )] pub struct Cli { #[command(subcommand)] pub command: Command, } #[derive(Debug, Subcommand)] pub enum Command { /// Find work, build it, boot it Kernel { #[command(subcommand)] command: KernelCommand, }, /// Carry a change from working tree to mailing list Patch { #[command(subcommand)] command: PatchCommand, }, } #[derive(Debug, Subcommand)] pub enum KernelCommand { /// Fetch a random open issue to work on Quest(QuestArgs), /// Build the kernel and boot it under qemu Test(TestArgs), } #[derive(Debug, Args)] pub struct QuestArgs { /// Print the picked issue as JSON #[arg(long)] pub json: bool, /// Only consider issues whose title contains this text #[arg(long, value_name = "TEXT")] pub filter: Option, } #[derive(Debug, Args)] pub struct TestArgs { /// Boot the existing build instead of rebuilding first #[arg(long)] pub no_build: bool, /// Rootfs image to boot with; defaults to the tree's own test image #[arg(long, value_name = "PATH")] pub rootfs: Option, /// Extra argument passed through to qemu (repeatable) #[arg(long = "qemu-arg", value_name = "ARG", allow_hyphen_values = true)] pub qemu_args: Vec, } #[derive(Debug, Subcommand)] pub enum PatchCommand { /// Run checkpatch.pl over the work in progress Check(CheckArgs), /// Auto-fix what checkpatch.pl reports Format(FormatArgs), /// Commit the work in progress with a kernel-style message Commit(CommitArgs), /// Write the diff against the base branch out to .patch Create(CreateArgs), /// Send a patch, with To/CC taken from get_maintainer.pl Submit(SubmitArgs), /// Re-roll a patch as v, renaming the file to match Update(UpdateArgs), } #[derive(Debug, Args)] pub struct CheckArgs { /// Check this patch file instead of the working tree #[arg(value_name = "PATCH")] pub patch: Option, /// Pass --strict to checkpatch.pl #[arg(long)] pub strict: bool, } #[derive(Debug, Args)] pub struct FormatArgs { /// Fix this patch file instead of the working tree #[arg(value_name = "PATCH")] pub patch: Option, } #[derive(Debug, Args)] pub struct CommitArgs { /// Commit message, without the Signed-off-by line #[arg(value_name = "MESSAGE")] pub message: String, /// Append your Signed-off-by, which the kernel requires #[arg(short = 's', long)] pub signoff: bool, /// Amend the previous commit instead of adding one #[arg(long)] pub amend: bool, } #[derive(Debug, Args)] pub struct CreateArgs { /// Patch name; `.patch` is appended if you leave it off #[arg(value_name = "NAME")] pub name: String, /// Branch or revision to diff against #[arg(long, value_name = "REV", default_value = "master")] pub base: String, /// Directory to write the patch into; defaults to the patch dir #[arg(long, value_name = "DIR")] pub out: Option, } #[derive(Debug, Args)] pub struct SubmitArgs { /// Patch file to send #[arg(value_name = "PATCH")] pub patch: PathBuf, /// Print the recipients and command instead of sending #[arg(long)] pub dry_run: bool, /// Extra address to CC (repeatable) #[arg(long = "cc", value_name = "ADDR")] pub cc: Vec, /// Message-Id of the patch this one re-rolls, so mail threads correctly #[arg(long, value_name = "MSGID")] pub in_reply_to: Option, } #[derive(Debug, Args)] pub struct UpdateArgs { /// Patch file to re-roll #[arg(value_name = "PATCH")] pub patch: PathBuf, /// Revision to re-roll as; defaults to one past the file's current vN #[arg(short = 'v', long, value_name = "N")] pub revision: Option, }