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