Skip to main content

commit_bridge/
error.rs

1//! Definitions for common error types.
2
3use crate::repository::RepositoryError;
4use axum::{
5    http::StatusCode,
6    response::{IntoResponse, Response},
7};
8use config::ConfigError;
9use rovo::aide::OperationOutput;
10use thiserror::Error;
11use validator::ValidationErrors;
12
13/// Validation error.
14#[derive(Debug, Error)]
15pub enum ValidationError {
16    /// Invalid field value.
17    #[error("Validation error: {0}")]
18    InvalidValue(String),
19}
20
21impl IntoResponse for ValidationError {
22    fn into_response(self) -> Response {
23        (StatusCode::UNPROCESSABLE_ENTITY, self.to_string()).into_response()
24    }
25}
26
27/// An error happened inside an Axum handler.
28#[derive(Debug, Error)]
29pub enum HandlerError {
30    /// Database query execution failure.
31    #[error("Repository Error: {0}")]
32    DbQuery(RepositoryError),
33
34    /// Requested resource not found.
35    #[error("Not Found")]
36    NotFound,
37}
38
39impl From<RepositoryError> for HandlerError {
40    fn from(err: RepositoryError) -> Self {
41        match err {
42            RepositoryError::NotFound => HandlerError::NotFound,
43            other => HandlerError::DbQuery(other),
44        }
45    }
46}
47
48impl OperationOutput for HandlerError {
49    type Inner = ();
50}
51
52impl IntoResponse for HandlerError {
53    fn into_response(self) -> Response {
54        let status = match self {
55            HandlerError::DbQuery(_) => StatusCode::INTERNAL_SERVER_ERROR,
56            HandlerError::NotFound => StatusCode::NOT_FOUND,
57        };
58        (status, self.to_string()).into_response()
59    }
60}
61
62/// An error that requires the server to be shut down.
63#[derive(Debug, Error)]
64pub enum FatalError {
65    /// Database is down or URL is incorrect.
66    #[error("Database connection: {0}")]
67    DbConnection(#[from] sqlx::Error),
68
69    /// Repository error.
70    #[error("Repository error: {0}")]
71    Repository(#[from] RepositoryError),
72
73    /// Error in database migration.
74    #[error("Database migration: {0}")]
75    Migration(#[from] sqlx::migrate::MigrateError),
76
77    /// Could not reserve an IP address with a TCP port
78    /// to connect to the server.
79    #[error("TCP binding: {0}")]
80    TcpBinding(#[source] std::io::Error),
81
82    /// I/O error during server's execution loop.
83    #[error("Serve: {0}")]
84    Serve(#[source] std::io::Error),
85
86    // Docs deferred to inner type.
87    #[allow(missing_docs)]
88    #[error("HTTP Client creation: {0}")]
89    ClientCreation(#[from] ClientCreationError),
90
91    /// Environment variable not set.
92    #[error("Environment variable '{0}' not set")]
93    EnvVarNotSet(String),
94
95    /// Could not load the authentication key.
96    #[error("Failed to load authentication key: {0}")]
97    AuthKeyLoading(#[source] jsonwebtoken::errors::Error),
98
99    /// Could not read the authentication key file.
100    #[error("Failed to read authentication key file: {0}")]
101    AuthKeyIo(#[source] std::io::Error),
102
103    /// Configuration is invalid.
104    #[error(transparent)]
105    Setup(SetupError),
106
107    /// Configuration validation failed.
108    #[error("Configuration validation failed: {0}")]
109    Validation(#[from] ValidationErrors),
110}
111
112/// Error about the setup configuration.
113#[derive(Debug, Error)]
114pub enum SetupError {
115    /// Configuration is incomplete.
116    #[error("Configuration error: {0}")]
117    Config(#[source] ConfigError),
118
119    /// Error in retrieving configuration from the environment.
120    #[error("Failed to load configuration: {0}")]
121    Env(#[source] dotenvy::Error),
122}
123
124impl From<config::ConfigError> for FatalError {
125    fn from(e: config::ConfigError) -> Self {
126        FatalError::Setup(SetupError::Config(e))
127    }
128}
129
130impl From<dotenvy::Error> for FatalError {
131    fn from(e: dotenvy::Error) -> Self {
132        FatalError::Setup(SetupError::Env(e))
133    }
134}
135
136/// HTTP Client creation failed.
137///
138/// The server cannot trigger workflows.
139#[derive(Debug, Error)]
140#[error(transparent)]
141pub struct ClientCreationError(#[from] reqwest::Error);
142
143/// Error in retrieving a commit or its info using `git ls-remote`.
144#[derive(Debug, Error)]
145pub enum CommitHashError {
146    /// Validation error.
147    #[error("Validation error: {0}")]
148    Validation(#[from] ValidationError),
149
150    /// I/O error while spawning the process.
151    #[error("I/O error in `git ls-remote`: {0}")]
152    Io(#[from] std::io::Error),
153
154    /// Unexpected exit status.
155    #[error("Unexpected `git ls-remote` exit status: {0}")]
156    UnexpectedStatus(String),
157
158    /// Unexpected output format.
159    #[error(
160        "Unexpected `git ls-remote` output format. Repo: {repo_url}; Branch: {branch}; Stdout: {stdout}"
161    )]
162    UnexpectedOutput {
163        /// The process output text.
164        stdout: String,
165        /// The relevant git repository URL.
166        repo_url: String,
167        /// The relevant git branch.
168        branch: String,
169    },
170}