1use 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#[derive(Debug, Error)]
15pub enum ValidationError {
16 #[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#[derive(Debug, Error)]
29pub enum HandlerError {
30 #[error("Repository Error: {0}")]
32 DbQuery(RepositoryError),
33
34 #[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#[derive(Debug, Error)]
64pub enum FatalError {
65 #[error("Database connection: {0}")]
67 DbConnection(#[from] sqlx::Error),
68
69 #[error("Repository error: {0}")]
71 Repository(#[from] RepositoryError),
72
73 #[error("Database migration: {0}")]
75 Migration(#[from] sqlx::migrate::MigrateError),
76
77 #[error("TCP binding: {0}")]
80 TcpBinding(#[source] std::io::Error),
81
82 #[error("Serve: {0}")]
84 Serve(#[source] std::io::Error),
85
86 #[allow(missing_docs)]
88 #[error("HTTP Client creation: {0}")]
89 ClientCreation(#[from] ClientCreationError),
90
91 #[error("Environment variable '{0}' not set")]
93 EnvVarNotSet(String),
94
95 #[error("Failed to load authentication key: {0}")]
97 AuthKeyLoading(#[source] jsonwebtoken::errors::Error),
98
99 #[error("Failed to read authentication key file: {0}")]
101 AuthKeyIo(#[source] std::io::Error),
102
103 #[error(transparent)]
105 Setup(SetupError),
106
107 #[error("Configuration validation failed: {0}")]
109 Validation(#[from] ValidationErrors),
110}
111
112#[derive(Debug, Error)]
114pub enum SetupError {
115 #[error("Configuration error: {0}")]
117 Config(#[source] ConfigError),
118
119 #[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#[derive(Debug, Error)]
140#[error(transparent)]
141pub struct ClientCreationError(#[from] reqwest::Error);
142
143#[derive(Debug, Error)]
145pub enum CommitHashError {
146 #[error("Validation error: {0}")]
148 Validation(#[from] ValidationError),
149
150 #[error("I/O error in `git ls-remote`: {0}")]
152 Io(#[from] std::io::Error),
153
154 #[error("Unexpected `git ls-remote` exit status: {0}")]
156 UnexpectedStatus(String),
157
158 #[error(
160 "Unexpected `git ls-remote` output format. Repo: {repo_url}; Branch: {branch}; Stdout: {stdout}"
161 )]
162 UnexpectedOutput {
163 stdout: String,
165 repo_url: String,
167 branch: String,
169 },
170}