Skip to main content

gunnar_sendpack/
error.rs

1//! One error type for the whole conversation.
2
3use std::fmt;
4
5/// What went wrong.
6#[derive(Debug, thiserror::Error)]
7pub enum Error {
8    /// The peer put something on the wire this grammar cannot read.
9    #[error("{0}")]
10    Protocol(String),
11    /// The caller asked for something that cannot be put on the wire.
12    ///
13    /// Separated from [`Error::Protocol`] because the two have different
14    /// audiences: a protocol error is the remote's fault and belongs in a
15    /// diagnostic, an invalid request is the caller's and belongs in a
16    /// compile-time-adjacent complaint.
17    #[error("{0}")]
18    Invalid(String),
19    /// The peer used a hash algorithm this repository does not.
20    ///
21    /// A variant of its own rather than a [`Error::Protocol`] string, because
22    /// it is the one protocol error a caller routinely wants to *re-report* in
23    /// its own vocabulary — an `object-format` mismatch is what a server tells
24    /// a client in its status report, and digging two algorithm names back out
25    /// of a formatted message to do so is how a message becomes an API by
26    /// accident.
27    #[error("an object id is {theirs} but the repository is {ours}")]
28    ObjectFormat {
29        /// The repository's algorithm, as it appears in `object-format=`.
30        ours: &'static str,
31        /// The peer's.
32        theirs: &'static str,
33    },
34    /// The remote refused the conversation and said why, in an `ERR <msg>`
35    /// packet or on sideband 3.
36    ///
37    /// Its own variant rather than an [`Error::Protocol`] string because it is
38    /// **not** a protocol error: the grammar was obeyed exactly. `ERR` is the
39    /// one line in the advertisement that is a sentence for a human rather than
40    /// a record for a parser, and reading it as a ref turns
41    /// `you are not allowed to push here` into
42    /// `bad object id "ERR" for ref "you are not allowed to push here"`.
43    ///
44    /// The wording is git's — `connect.c` answers an `ERR` packet with
45    /// `die(_("remote error: %s"), arg)` — so an operator who has seen the
46    /// message from git does not have to learn a second phrase for it. Measured
47    /// against git 2.53.0: `fatal: remote error: you are not allowed to push
48    /// here`, exit 128.
49    #[error("remote error: {0}")]
50    Remote(String),
51    /// The socket, the pipe or the file underneath.
52    #[error("io: {0}")]
53    Io(#[from] std::io::Error),
54}
55
56impl Error {
57    /// A protocol error from anything printable.
58    pub fn protocol(message: impl fmt::Display) -> Self {
59        Error::Protocol(message.to_string())
60    }
61
62    /// An invalid-request error from anything printable.
63    pub fn invalid(message: impl fmt::Display) -> Self {
64        Error::Invalid(message.to_string())
65    }
66}
67
68/// The crate's result type.
69pub type Result<T> = std::result::Result<T, Error>;