use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::response::Response;
use axum::Json;
use hyper::Error as HyperError;
use notify::Error as NotifyError;
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(#[from] HyperError),
#[error("miniflare encountered an error: {0}")]
MiniflareCommandError(IoError),
#[error("miniflare encountered an error\ncause:\n{0}")]
MiniflareError(String),
#[error(transparent)]
SchemaParserError(IoError),
#[error(transparent)]
SchemaParserResultRead(IoError),
#[error("schema parser result is malformed JSON:\n{0}")]
SchemaParserResultJson(serde_json::Error),
#[error(transparent)]
SchemaRegistryWrite(IoError),
#[error("could not create a temporary file for the parser result: {0}")]
CreateTemporaryFile(IoError),
#[error("could not create an output artifact file during a resolver build")]
CreateResolverArtifactFile(IoError),
#[error("could not extract the resolver wrapper worker contents")]
ExtractResolverWrapperWorkerContents(String),
#[error("could not parse grafbase/schema.graphql\n{0}")]
ParseSchema(String),
#[error("could not find a resolver referenced in the schema under the path {0}.{{js,ts}}")]
ResolverDoesNotExist(PathBuf),
#[error("npm encountered an error: {0}")]
NpmCommandError(IoError),
#[error("npm failed with output:\n{0}")]
NpmCommand(String),
#[error("resolver {0} failed to build:\n{1}")]
ResolverBuild(String, 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 create a project database directory\ncaused by: {0}")]
CreateDatabaseDir(IoError),
#[error("could not read the project database directory\ncaused by: {0}")]
ReadDatabaseDir(IoError),
#[error("could not find an available port for the bridge server")]
AvailablePort,
#[error(transparent)]
SpawnedTaskPanic(#[from] 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,
#[error("Could not initialize a file watcher: {0}")]
FileWatcherInit(#[from] NotifyError),
}
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 IntoResponse for ServerError {
fn into_response(self) -> Response {
let body = Json(json!({
"error": self.to_string(),
}));
(StatusCode::INTERNAL_SERVER_ERROR, body).into_response()
}
}