/ src / api / error.rs
error.rs
  1  use axum::http::StatusCode;
  2  use axum::response::{IntoResponse, Response};
  3  use axum::Json;
  4  use serde_json::json;
  5  
  6  use radicle::git::raw::ErrorExt;
  7  
  8  /// Errors relating to the API backend.
  9  #[derive(Debug, thiserror::Error)]
 10  pub enum Error {
 11      /// The entity was not found.
 12      #[error("entity not found")]
 13      NotFound,
 14  
 15      /// An error occurred with env variables.
 16      #[error(transparent)]
 17      Env(#[from] std::env::VarError),
 18  
 19      /// Profile error.
 20      #[error(transparent)]
 21      Profile(#[from] radicle::profile::Error),
 22  
 23      /// Crypto error.
 24      #[error(transparent)]
 25      Crypto(#[from] radicle::crypto::Error),
 26  
 27      /// Storage error.
 28      #[error(transparent)]
 29      Storage(#[from] radicle::storage::Error),
 30  
 31      /// Cob cache error.
 32      #[error(transparent)]
 33      CobCache(#[from] radicle::cob::cache::Error),
 34  
 35      /// Cob issue cache error.
 36      #[error(transparent)]
 37      CacheIssue(#[from] radicle::cob::issue::cache::Error),
 38  
 39      /// Cob issue error.
 40      #[error(transparent)]
 41      CobIssue(#[from] radicle::cob::issue::Error),
 42  
 43      /// Cob patch error.
 44      #[error(transparent)]
 45      CobPatch(#[from] radicle::cob::patch::Error),
 46  
 47      /// Cob patch cache error.
 48      #[error(transparent)]
 49      CachePatch(#[from] radicle::cob::patch::cache::Error),
 50  
 51      /// Cob store error.
 52      #[error(transparent)]
 53      CobStore(#[from] radicle::cob::store::Error),
 54  
 55      /// Repository error.
 56      #[error(transparent)]
 57      Repository(#[from] radicle::storage::RepositoryError),
 58  
 59      /// Routing error.
 60      #[error(transparent)]
 61      Routing(#[from] radicle::node::routing::Error),
 62  
 63      /// Project doc error.
 64      #[error(transparent)]
 65      ProjectDoc(#[from] radicle::identity::doc::PayloadError),
 66  
 67      /// Surf directory error.
 68      #[error(transparent)]
 69      SurfDir(#[from] radicle_surf::fs::error::Directory),
 70  
 71      /// Surf error.
 72      #[error(transparent)]
 73      Surf(#[from] radicle_surf::Error),
 74  
 75      /// Git2 error.
 76      #[error(transparent)]
 77      Git2(#[from] radicle::git::raw::Error),
 78  
 79      /// Storage refs error.
 80      #[error(transparent)]
 81      StorageRef(#[from] radicle::storage::refs::Error),
 82  
 83      /// Identity doc error.
 84      #[error(transparent)]
 85      IdentityDoc(#[from] radicle::identity::doc::DocError),
 86  
 87      /// Tracking store error.
 88      #[error(transparent)]
 89      TrackingStore(#[from] radicle::node::policy::store::Error),
 90  
 91      /// Node database error.
 92      #[error(transparent)]
 93      Database(#[from] radicle::node::db::Error),
 94  
 95      /// Node error.
 96      #[error(transparent)]
 97      Node(#[from] radicle::node::Error),
 98  }
 99  
100  impl IntoResponse for Error {
101      fn into_response(self) -> Response {
102          let message = self.to_string();
103          let (status, msg) = match self {
104              Error::NotFound => (StatusCode::NOT_FOUND, None),
105              Error::CobStore(e @ radicle::cob::store::Error::NotFound(_, _)) => {
106                  (StatusCode::NOT_FOUND, Some(e.to_string()))
107              }
108              Error::Crypto(msg) => (StatusCode::BAD_REQUEST, Some(msg.to_string())),
109              Error::Surf(radicle_surf::Error::Git(e)) if e.is_not_found() => {
110                  (StatusCode::NOT_FOUND, Some(e.message().to_owned()))
111              }
112              Error::Surf(radicle_surf::Error::Directory(
113                  e @ radicle_surf::fs::error::Directory::PathNotFound(_),
114              )) => (StatusCode::NOT_FOUND, Some(e.to_string())),
115              Error::Git2(e) if e.is_not_found() => {
116                  (StatusCode::NOT_FOUND, Some(e.message().to_owned()))
117              }
118              Error::Git2(e) => (
119                  StatusCode::INTERNAL_SERVER_ERROR,
120                  Some(e.message().to_owned()),
121              ),
122              Error::Storage(err) if err.is_not_found() => {
123                  (StatusCode::NOT_FOUND, Some(err.to_string()))
124              }
125              Error::Repository(err) if err.is_not_found() => {
126                  (StatusCode::NOT_FOUND, Some(err.to_string()))
127              }
128              Error::StorageRef(err) if err.is_not_found() => {
129                  (StatusCode::NOT_FOUND, Some(err.to_string()))
130              }
131              other => {
132                  tracing::error!("Error: {message}");
133                  tracing::debug!("Error Debug: {:?}", other);
134  
135                  if cfg!(debug_assertions) {
136                      (StatusCode::INTERNAL_SERVER_ERROR, Some(other.to_string()))
137                  } else {
138                      (StatusCode::INTERNAL_SERVER_ERROR, None)
139                  }
140              }
141          };
142  
143          let body = Json(json!({
144              "error": msg.or_else(|| status.canonical_reason().map(|r| r.to_string())),
145              "code": status.as_u16()
146          }));
147  
148          (status, body).into_response()
149      }
150  }