ferro_cargo_registry_server/
error.rs1use axum::http::StatusCode;
9use axum::response::{IntoResponse, Response};
10use ferro_blob_store::BlobStoreError;
11use serde_json::json;
12
13#[derive(Debug, thiserror::Error)]
15pub enum CargoError {
16 #[error("invalid crate name: {0}")]
19 InvalidName(String),
20
21 #[error("invalid semver: {0}")]
23 InvalidVersion(String),
24
25 #[error("invalid publish payload: {0}")]
28 InvalidPublish(String),
29
30 #[error("checksum mismatch: declared {declared}, computed {computed}")]
32 ChecksumMismatch {
33 declared: String,
35 computed: String,
37 },
38
39 #[error("not found: {0}")]
41 NotFound(String),
42
43 #[error("not implemented: {0}")]
45 NotImplemented(String),
46
47 #[error(transparent)]
49 Storage(#[from] BlobStoreError),
50}
51
52impl CargoError {
53 #[must_use]
55 pub fn status(&self) -> StatusCode {
56 match self {
57 Self::InvalidName(_)
58 | Self::InvalidVersion(_)
59 | Self::InvalidPublish(_)
60 | Self::ChecksumMismatch { .. } => StatusCode::BAD_REQUEST,
61 Self::NotFound(_) => StatusCode::NOT_FOUND,
62 Self::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
63 Self::Storage(err) => storage_status(err),
64 }
65 }
66}
67
68fn storage_status(err: &BlobStoreError) -> StatusCode {
69 match err {
70 BlobStoreError::NotFound(_) => StatusCode::NOT_FOUND,
71 BlobStoreError::DigestMismatch { .. } | BlobStoreError::InvalidDigest(_) => {
72 StatusCode::BAD_REQUEST
73 }
74 BlobStoreError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
75 _ => StatusCode::INTERNAL_SERVER_ERROR,
76 }
77}
78
79impl IntoResponse for CargoError {
80 fn into_response(self) -> Response {
81 let status = self.status();
82 let body = json!({
83 "errors": [{ "detail": self.to_string() }]
84 });
85 (status, axum::Json(body)).into_response()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::CargoError;
92 use axum::http::StatusCode;
93
94 #[test]
95 fn invalid_name_is_400() {
96 assert_eq!(
97 CargoError::InvalidName(String::new()).status(),
98 StatusCode::BAD_REQUEST
99 );
100 }
101
102 #[test]
103 fn not_found_is_404() {
104 assert_eq!(
105 CargoError::NotFound("x".into()).status(),
106 StatusCode::NOT_FOUND
107 );
108 }
109
110 #[test]
111 fn not_implemented_is_501() {
112 assert_eq!(
113 CargoError::NotImplemented("git".into()).status(),
114 StatusCode::NOT_IMPLEMENTED
115 );
116 }
117
118 #[test]
119 fn checksum_mismatch_is_400() {
120 let e = CargoError::ChecksumMismatch {
121 declared: "a".into(),
122 computed: "b".into(),
123 };
124 assert_eq!(e.status(), StatusCode::BAD_REQUEST);
125 }
126}