use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use ferro_blob_store::BlobStoreError;
use serde_json::json;
#[derive(Debug, thiserror::Error)]
pub enum CargoError {
#[error("invalid crate name: {0}")]
InvalidName(String),
#[error("invalid semver: {0}")]
InvalidVersion(String),
#[error("invalid publish payload: {0}")]
InvalidPublish(String),
#[error("checksum mismatch: declared {declared}, computed {computed}")]
ChecksumMismatch {
declared: String,
computed: String,
},
#[error("not found: {0}")]
NotFound(String),
#[error("not implemented: {0}")]
NotImplemented(String),
#[error(transparent)]
Storage(#[from] BlobStoreError),
}
impl CargoError {
#[must_use]
pub fn status(&self) -> StatusCode {
match self {
Self::InvalidName(_)
| Self::InvalidVersion(_)
| Self::InvalidPublish(_)
| Self::ChecksumMismatch { .. } => StatusCode::BAD_REQUEST,
Self::NotFound(_) => StatusCode::NOT_FOUND,
Self::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
Self::Storage(err) => storage_status(err),
}
}
}
fn storage_status(err: &BlobStoreError) -> StatusCode {
match err {
BlobStoreError::NotFound(_) => StatusCode::NOT_FOUND,
BlobStoreError::DigestMismatch { .. } | BlobStoreError::InvalidDigest(_) => {
StatusCode::BAD_REQUEST
}
BlobStoreError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
impl IntoResponse for CargoError {
fn into_response(self) -> Response {
let status = self.status();
let body = json!({
"errors": [{ "detail": self.to_string() }]
});
(status, axum::Json(body)).into_response()
}
}
#[cfg(test)]
mod tests {
use super::CargoError;
use axum::http::StatusCode;
#[test]
fn invalid_name_is_400() {
assert_eq!(
CargoError::InvalidName(String::new()).status(),
StatusCode::BAD_REQUEST
);
}
#[test]
fn not_found_is_404() {
assert_eq!(
CargoError::NotFound("x".into()).status(),
StatusCode::NOT_FOUND
);
}
#[test]
fn not_implemented_is_501() {
assert_eq!(
CargoError::NotImplemented("git".into()).status(),
StatusCode::NOT_IMPLEMENTED
);
}
#[test]
fn checksum_mismatch_is_400() {
let e = CargoError::ChecksumMismatch {
declared: "a".into(),
computed: "b".into(),
};
assert_eq!(e.status(), StatusCode::BAD_REQUEST);
}
}