Files
spectral/src/error.rs
T
huntedbytheirs 97664806b7 patch: fill in the loop verbs, and test them
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.
2026-09-16 18:41:51 -04:00

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),
}