Every patch verb outside the series work is real. check reads the working tree through checkpatch's stdin, which is the only way it can read one, since --git with no revision dies; format puts --fix-inplace's output back into the tree after setting the uncommitted change aside, and restores it if the fix does not apply; commit and create sit on git; create writes a mail-formatted patch, because git send-email refuses a bare diff with "No subject line"; submit splits get_maintainer.pl's roles into To and Cc, prints the command line it is about to run, and only sends when --dry-run is off; update re-rolls a patch by renaming it and does nothing at the revision it is already at. git.rs grows the plumbing those verbs stand on: diff_unstaged, format_patch, apply, commit, rev_parse. apply feeds the patch in on stdin, so a fixed patch stays in memory until it is known to apply. tests/ holds a fixture kernel tree, so the suite needs no kernel tree, no network, and no configured git send-email. The two tests that do want the host's own checkpatch and get_maintainer are ignored unless asked for, and one of them earned its place immediately: checkpatch's --file means "this argument is source code", so checking a patch with it reported a dirty patch as clean, 8004 lines and no errors. Patchfile mode is checkpatch's default and the path now stands alone. patch check also takes --rev, which is how a committed range gets checked. Checked with cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test, and cargo doc --no-deps.
86 lines
2.9 KiB
Rust
86 lines
2.9 KiB
Rust
//! One error type for the whole CLI.
|
|
//!
|
|
//! Everything that can go wrong ends up as an [`Error`], gets a `?` at the
|
|
//! call site, and is printed once by `main`.
|
|
|
|
use std::path::PathBuf;
|
|
use std::process::ExitStatus;
|
|
|
|
/// Shorthand for the crate's result type.
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|
|
|
/// How a failed process is described in [`Error::ExternalCommand`].
|
|
pub(crate) fn exit_code(status: &ExitStatus) -> String {
|
|
status
|
|
.code()
|
|
.map_or_else(|| "signal".to_owned(), |code| code.to_string())
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum Error {
|
|
/// Nothing at the configured path at all.
|
|
#[error("no kernel tree at `{path}`; set ${env} or clone one there")]
|
|
KernelTreeMissing { path: PathBuf, env: &'static str },
|
|
|
|
/// Something is there, but it is not a kernel source tree.
|
|
#[error("`{path}` is not a kernel source tree (no scripts/checkpatch.pl)")]
|
|
NotAKernelTree { path: PathBuf },
|
|
|
|
/// An external tool ran and failed.
|
|
#[error("`{program} {args}` failed (exit {code}):\n{stderr}")]
|
|
ExternalCommand {
|
|
program: String,
|
|
args: String,
|
|
code: String,
|
|
stderr: String,
|
|
},
|
|
|
|
/// An external tool could not even be started.
|
|
#[error("could not run `{program}`")]
|
|
CommandSpawn {
|
|
program: String,
|
|
#[source]
|
|
source: std::io::Error,
|
|
},
|
|
|
|
/// A patch file the user named does not exist.
|
|
#[error("no patch named `{name}` in `{dir}`")]
|
|
PatchNotFound { name: String, dir: PathBuf },
|
|
|
|
/// A revision named on the command line does not resolve.
|
|
#[error("no revision `{rev}` in this tree; `--base` wants a branch or commit that exists")]
|
|
UnknownRevision { rev: String },
|
|
|
|
/// The base is already at the tip, so there is no change to send.
|
|
#[error("nothing to send: `{base}` and HEAD hold no commits; commit the change first")]
|
|
NoCommitsToSend { base: String },
|
|
|
|
/// One patch file was asked to carry a whole set.
|
|
#[error(
|
|
"`{base}..HEAD` holds {count} commits; one patch file cannot carry a set (patch series support is not built yet)"
|
|
)]
|
|
RangeHoldsSeveralCommits { base: String, count: usize },
|
|
|
|
/// `--fix` was pointed at something that is not a patch file.
|
|
#[error(
|
|
"checkpatch only fixes a patch file; give `patch format` a patch instead of a revision"
|
|
)]
|
|
FixNeedsPatchFile,
|
|
|
|
/// checkpatch's fix could not be put into the working tree.
|
|
#[error("the fix could not be applied ({reason}); the working tree was left as it was")]
|
|
FixApplyFailed { reason: String },
|
|
|
|
/// Re-rolling would land on a file that is already there.
|
|
#[error("`{path}` already exists; delete it or pick another revision")]
|
|
RerollWouldOverwrite { path: PathBuf },
|
|
|
|
#[error(transparent)]
|
|
Io(#[from] std::io::Error),
|
|
|
|
// Reached once `kernel quest` stops being a stub.
|
|
#[allow(dead_code)]
|
|
#[error(transparent)]
|
|
Http(#[from] reqwest::Error),
|
|
}
|