Skip to main content

git_bot_feedback/
error.rs

1//! Error types used across the git-bot-feedback crate.
2#[cfg(feature = "file-changes")]
3use std::path::PathBuf;
4
5use chrono::{DateTime, Utc};
6use thiserror::Error;
7
8use crate::client::MAX_RETRIES;
9
10/// The possible errors emitted when parsing git diffs.
11#[derive(Debug, thiserror::Error)]
12#[cfg(feature = "file-changes")]
13#[cfg_attr(docsrs, doc(cfg(feature = "file-changes")))]
14pub enum DiffError {
15    /// An error emitted when failing to compile a Regular expression pattern.
16    #[error("Failed to compile regex pattern: {0}")]
17    RegExCompileFailed(#[from] regex::Error),
18}
19
20/// The possible errors emitted when validating an [`OutputVariable`](struct@crate::OutputVariable).
21#[derive(Debug, thiserror::Error, PartialEq, Eq)]
22pub enum OutputVariableError {
23    /// The output variable's name is empty.
24    #[error("The output variable's name is empty")]
25    NameIsEmpty,
26    /// The output variable's name starts with a number.
27    #[error("The output variable's name starts with a number: '{0}'")]
28    NameStartsWithNumber(String),
29    /// The output variable's name contains non-printable characters.
30    #[error("The output variable's name contains non-printable characters: '{0}'")]
31    NameContainsNonPrintableCharacters(String),
32    /// The output variable's value contains non-printable characters.
33    #[error("The output variable's value contains non-printable characters: '{0}'")]
34    ValueContainsNonPrintableCharacters(String),
35    /// Unsupported CI platform.
36    #[error("Unsupported CI platform")]
37    UnsupportedPlatform,
38}
39
40/// The possible error emitted by the REST client API
41#[derive(Debug, Error)]
42pub enum RestClientError {
43    /// Errors related to parsing git diffs.
44    #[error(transparent)]
45    #[cfg(feature = "file-changes")]
46    #[cfg_attr(docsrs, doc(cfg(feature = "file-changes")))]
47    DiffError(#[from] DiffError),
48
49    /// Error emitted when encountering malformed event information.
50    #[error("Encountered malformed event info: {0}")]
51    MalformedEventInfo(String),
52
53    /// Error related to making HTTP requests
54    #[error(transparent)]
55    Request(#[from] reqwest::Error),
56
57    /// Error related to making HTTP requests, with additional context about the request that caused the error.
58    #[error("Failed to {task}: {source}")]
59    RequestContext {
60        /// The task being attempted.
61        task: String,
62        /// The original error being propagated.
63        #[source]
64        source: reqwest::Error,
65    },
66
67    /// Errors related to standard I/O.
68    #[error("Failed to {task}: {source}")]
69    Io {
70        /// The task being attempted.
71        task: String,
72        /// The original error being propagated.
73        #[source]
74        source: std::io::Error,
75    },
76
77    /// Error related to `git` command execution.
78    #[error("Git command error: {0}")]
79    #[cfg(feature = "file-changes")]
80    #[cfg_attr(docsrs, doc(cfg(feature = "file-changes")))]
81    GitCommand(String),
82
83    /// Error related to exceeding REST API Rate limits and
84    /// no reset time is provided in the response headers.
85    #[error("Primary Rate Limit exceeded (no reset time provided)")]
86    RateLimitNoReset,
87
88    /// Error related to exceeding REST API Rate limits with a known reset time.
89    #[error("Primary Rate Limit exceeded; resets at {0}")]
90    RateLimitPrimary(DateTime<Utc>),
91
92    /// Error related to exhausting all retries after hitting REST API Rate limits.
93    #[error("Rate Limit exceeded after all {MAX_RETRIES} retries exhausted")]
94    RateLimitSecondary,
95
96    /// Error emitted when failing to clone a request object.
97    #[error("Failed to clone request object for auto-retries")]
98    CannotCloneRequest,
99
100    /// Error emitted when failing to create a header value.
101    #[error("Tried to create a header value from invalid string data")]
102    InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
103
104    /// Error emitted when failing to convert a header value to string.
105    #[error("Failed to convert header value to string")]
106    UnexpectedHeaderValue(#[from] reqwest::header::ToStrError),
107
108    /// Error emitted when failing to parse an integer from a header value (as a UTF-8 string).
109    #[error("Failed to parse integer from header value: {0}")]
110    HeaderParseInt(#[from] std::num::ParseIntError),
111
112    /// Error emitted when failing to parse a URL.
113    #[error("Failed to parse URL:{0}")]
114    UrlParse(#[from] url::ParseError),
115
116    /// Error emitted when failing to deserialize/serialize request/response JSON data.
117    #[error("Failed to {task}: {source}")]
118    Json {
119        /// The task being attempted.
120        task: String,
121        /// The original error being propagated.
122        #[source]
123        source: serde_json::Error,
124    },
125
126    /// Error emitted when failing to read an environment variable.
127    #[error("Failed to get env var '{name}': {source}")]
128    EnvVar {
129        /// The name of the environment variable that was attempted to be read.
130        name: String,
131        /// The original error being propagated.
132        #[source]
133        source: std::env::VarError,
134    },
135
136    /// An error emitted when encountering an invalid [`OutputVariable`](crate::output_variable::OutputVariable).
137    #[error("OutputVariable is malformed: {0}")]
138    OutputVar(#[from] OutputVariableError),
139}
140
141impl RestClientError {
142    /// Helper function to create an [`Self::EnvVar`] error with variable name and source error.
143    pub fn env_var(name: &str, source: std::env::VarError) -> Self {
144        Self::EnvVar {
145            name: name.to_string(),
146            source,
147        }
148    }
149
150    /// Helper function to create an [`Self::Io`] error with task context.
151    pub fn io(task: &str, source: std::io::Error) -> Self {
152        Self::Io {
153            task: task.to_string(),
154            source,
155        }
156    }
157
158    /// Builder function to add context to [`Self::Request`] errors.
159    ///
160    /// Returns a [`Self::RequestContext`] error if `self` is a [`Self::Request`] error.
161    /// Otherwise, returns `self` unchanged.
162    pub fn add_request_context(self, task: &str) -> Self {
163        match self {
164            Self::Request(e) => Self::RequestContext {
165                task: task.to_string(),
166                source: e,
167            },
168            _ => self,
169        }
170    }
171
172    /// Helper function to create a [`Self::Json`] error with task context.
173    pub fn json(task: &str, source: serde_json::Error) -> Self {
174        Self::Json {
175            task: task.to_string(),
176            source,
177        }
178    }
179}
180
181/// The possible errors emitted by file system operations.
182///
183/// This is only used (via [`FileFilter::walk()`](fn@crate::file_utils::file_filter::FileFilter::walk_dir));
184/// typically when not running within a supported CI environment.
185#[cfg(feature = "file-changes")]
186#[derive(Debug, Error)]
187#[cfg_attr(docsrs, doc(cfg(feature = "file-changes")))]
188pub enum DirWalkError {
189    /// Error emitted when failing to read a directory entry.
190    #[error("Failed to read {path}: {source}")]
191    ReadDir {
192        /// The path that was attempted to be read.
193        path: PathBuf,
194        /// The original error being propagated.
195        #[source]
196        source: std::io::Error,
197    },
198
199    /// Error emitted when failing to interact with files.
200    #[error(transparent)]
201    OsError(#[from] std::io::Error),
202}
203
204#[cfg(test)]
205mod tests {
206    use super::RestClientError;
207
208    #[test]
209    fn no_added_req_ctx() {
210        let err = RestClientError::CannotCloneRequest;
211        assert!(matches!(
212            err.add_request_context("some task"),
213            RestClientError::CannotCloneRequest
214        ));
215    }
216}