Skip to main content

fluidattacks_core_git/
errors.rs

1use std::io;
2use std::process::ExitStatus;
3use std::time::Duration;
4
5#[derive(Debug, thiserror::Error)]
6pub enum GitCallError {
7    #[error("git {command} could not start: {source}")]
8    NotStarted {
9        command: &'static str,
10        source: io::Error,
11    },
12    #[error("git {command} did not finish within {after:?}")]
13    TimedOut {
14        command: &'static str,
15        after: Duration,
16    },
17    /// git ran and refused, as it does for a missing repository or revision.
18    #[error("git {command} exited with {status}: {stderr}")]
19    Refused {
20        command: &'static str,
21        status: ExitStatus,
22        stderr: String,
23    },
24    /// No process can receive a NUL, so std turns the call down before it starts.
25    #[error("git {command} got an argument with an embedded null byte")]
26    NullByte { command: &'static str },
27    #[error("git {command} printed something unreadable: {reason}")]
28    Malformed {
29        command: &'static str,
30        reason: String,
31    },
32}
33
34/// Why `download_and_extract_repo` failed, by the step that failed.
35#[derive(Debug, thiserror::Error)]
36pub enum DownloadError {
37    /// The parent of the destination, or the archive inside the work
38    /// directory, could not be created or written.
39    #[error("writing the archive: {0}")]
40    Write(#[source] io::Error),
41    #[error(transparent)]
42    Fetch(#[from] FetchError),
43    #[error(transparent)]
44    Extract(#[from] ExtractError),
45    /// `git reset --hard` did not restore the working tree.
46    #[error(transparent)]
47    Reset(#[from] GitCallError),
48    /// Removing symlinks or out-of-scope files failed; `errno` is the one of
49    /// the file call that failed, when there was one.
50    #[error("pruning the working tree: {message}")]
51    Prune { message: String, errno: Option<i32> },
52    /// The work directory beside the destination could not be created, or the
53    /// repository could not take the place of the destination.
54    #[error("placing the repository: {0}")]
55    Place(#[source] io::Error),
56}
57
58/// How the request for the archive failed.
59#[derive(Debug, thiserror::Error)]
60pub enum FetchError {
61    #[error("invalid url {url}: {reason}")]
62    InvalidUrl { url: String, reason: String },
63    #[error("not an http url: {0}")]
64    NonHttpUrl(String),
65    /// DNS, a refused connection or a TLS handshake that failed.
66    #[error("could not connect: {0}")]
67    Connect(#[source] reqwest::Error),
68    #[error("connecting timed out: {0}")]
69    ConnectTimeout(#[source] reqwest::Error),
70    /// The total timeout ran out before the response headers arrived.
71    #[error("no response in time: {0}")]
72    Timeout(#[source] reqwest::Error),
73    /// The server closed the connection before it answered.
74    #[error("the server disconnected: {0}")]
75    Disconnected(#[source] reqwest::Error),
76    /// The socket failed while the request was sent; `errno` is the one of
77    /// the socket call.
78    #[error("the socket failed: {source}")]
79    Socket {
80        reset: bool,
81        errno: Option<i32>,
82        source: reqwest::Error,
83    },
84    #[error("malformed response: {0}")]
85    Malformed(#[source] reqwest::Error),
86    #[error("the server answered {0}")]
87    Status(reqwest::StatusCode),
88    /// The body ended before its length, or the connection broke mid-read.
89    #[error("the body was cut: {0}")]
90    BodyCut(#[source] reqwest::Error),
91    /// The total timeout ran out while the body was read.
92    #[error("the body did not arrive in time: {0}")]
93    BodyTimeout(#[source] reqwest::Error),
94    #[error("the request failed: {0}")]
95    Other(#[source] reqwest::Error),
96}
97
98/// How turning the archive into a repository failed.
99#[derive(Debug, thiserror::Error)]
100pub enum ExtractError {
101    /// The archive is not gzip, its tar or deflate data is broken, or its
102    /// checksum does not match.
103    #[error("corrupt archive: {0}")]
104    Corrupt(#[source] io::Error),
105    /// The gzip stream ends before its trailer.
106    #[error("truncated archive: {0}")]
107    Truncated(#[source] io::Error),
108    /// The archive holds no top directory with a real `.git` directory.
109    #[error("the archive holds no repository: {0}")]
110    NoRepository(String),
111    #[error("extracting the archive: {0}")]
112    Io(#[source] io::Error),
113}