1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::response::Response;
use axum::Json;
use common::traits::ToExitCode;
use hyper::Error as HyperError;
use serde_json::json;
use sqlx::Error as SqlxError;
use std::io::Error as IoError;
use std::path::PathBuf;
use thiserror::Error;
use tokio::task::JoinError;
#[derive(Error, Debug)]
pub enum ServerError {
#[error("could not create path '{0}' for the embedded server files")]
CreateDir(PathBuf),
#[error("could not write an embedded server file: {0}")]
WriteFile(String),
#[error("could not read the previously extracted embedded file versions")]
ReadVersion,
#[error("could not connect to the sqlite database: {0}")]
ConnectToDatabase(SqlxError),
#[error("could not create an sqlite database file: {0}")]
CreateDatabase(SqlxError),
#[error("could not query the sqlite database: {0}")]
QueryDatabase(SqlxError),
#[error("encountered an unknown sqlite error: {0}")]
UnknownSqliteError(SqlxError),
#[error("the bridge api encountered an error: {0}")]
BridgeApi(HyperError),
#[error("miniflare encountered an error: {0}")]
MiniflareCommandError(IoError),
#[error("miniflare encountered an error\ncause:\n{0}")]
MiniflareError(String),
#[error(transparent)]
SchemaParserError(IoError),
#[error("could not parse grafbase/schema.graphql\n{0}")]
ParseSchema(String),
#[error("non utf-8 path used for project")]
ProjectPath,
#[error("$HOME/.grafbase is a non utf8 path")]
CachePath,
#[error("could not create a project cache directory")]
CreateCacheDir,
#[error("could not find an available port for the bridge server")]
AvailablePort,
#[error(transparent)]
SpawnedTaskPanic(JoinError),
#[error("Node.js does not seem to be installed")]
NodeInPath,
#[error("Node.js version {0} is unsupported")]
OutdatedNode(String, String),
#[error("Could not retrive the installed version of Node.js")]
CheckNodeVersion,
}
impl ToExitCode for ServerError {
fn to_exit_code(&self) -> i32 {
match &self {
Self::CreateDir(_)
| Self::CreateCacheDir
| Self::WriteFile(_)
| Self::ReadVersion
| Self::ParseSchema(_)
| Self::NodeInPath
| Self::OutdatedNode(_, _) => exitcode::DATAERR,
Self::CreateDatabase(_)
| Self::QueryDatabase(_)
| Self::BridgeApi(_)
| Self::ConnectToDatabase(_)
| Self::UnknownSqliteError(_)
| Self::MiniflareCommandError(_)
| Self::MiniflareError(_)
| Self::SpawnedTaskPanic(_)
| Self::SchemaParserError(_)
| Self::CheckNodeVersion => exitcode::SOFTWARE,
Self::ProjectPath | Self::CachePath => exitcode::CANTCREAT,
Self::AvailablePort => exitcode::UNAVAILABLE,
}
}
}
impl From<SqlxError> for ServerError {
fn from(error: SqlxError) -> Self {
match error {
SqlxError::RowNotFound
| SqlxError::TypeNotFound { .. }
| SqlxError::ColumnNotFound(_)
| SqlxError::ColumnDecode { .. }
| SqlxError::ColumnIndexOutOfBounds { .. }
| SqlxError::Io(_)
| SqlxError::Decode(_)
| SqlxError::Database(_) => Self::QueryDatabase(error),
SqlxError::Configuration(_)
| SqlxError::Tls(_)
| SqlxError::PoolTimedOut
| SqlxError::Protocol(_)
| SqlxError::PoolClosed
| SqlxError::WorkerCrashed => Self::ConnectToDatabase(error),
SqlxError::Migrate(_) => Self::CreateDatabase(error),
_ => Self::UnknownSqliteError(error),
}
}
}
impl From<HyperError> for ServerError {
fn from(error: HyperError) -> Self {
Self::BridgeApi(error)
}
}
impl IntoResponse for ServerError {
fn into_response(self) -> Response {
let body = Json(json!({
"error": self.to_string(),
}));
(StatusCode::INTERNAL_SERVER_ERROR, body).into_response()
}
}
impl From<JoinError> for ServerError {
fn from(error: JoinError) -> Self {
Self::SpawnedTaskPanic(error)
}
}