sos_server/
error.rs

1//! Error type for the server.
2use axum::{
3    body::Body,
4    http::StatusCode,
5    response::{IntoResponse, Json, Response},
6};
7use serde_json::{json, Value};
8use sos_core::AccountId;
9use std::path::PathBuf;
10use thiserror::Error;
11
12/// Errors generated by the server module.
13#[derive(Debug, Error)]
14pub enum Error {
15    /// HTTP status code.
16    #[error("{0}")]
17    Status(StatusCode),
18
19    /// Status code with JSON response.
20    #[error("{0} {1}")]
21    Json(StatusCode, Value),
22
23    /// Unauthorized error.
24    #[error("unauthorized, may need to retry the protocol handshake")]
25    Unauthorized,
26
27    /// Bad request error.
28    #[error("bad request")]
29    BadRequest,
30
31    /// Forbidden access.
32    #[error("forbidden")]
33    Forbidden,
34
35    /// Conflict.
36    #[error("conflict")]
37    Conflict,
38
39    /// Error generated when an RPC method is not supported.
40    #[error("unknown rpc method '{0}'")]
41    RpcUnknownMethod(String),
42
43    /// Error generated when a path is not a file.
44    #[error("path {0} is not a file")]
45    NotFile(PathBuf),
46
47    /// Error generated when a path is not a directory.
48    #[error("not a directory {0}")]
49    NotDirectory(PathBuf),
50
51    /// Error generated when a directory already exists.
52    #[error("directory {0} already exists")]
53    DirectoryExists(PathBuf),
54
55    /// Error generated when a file already exists.
56    #[error("file {0} already exists")]
57    FileExists(PathBuf),
58
59    /// Error generated when an account is required.
60    #[error("account '{0}' does not exist")]
61    NoAccount(AccountId),
62
63    /// Error generated when an account should not already exist.
64    #[error("account '{0}' already exists")]
65    AccountExists(AccountId),
66
67    /// Error generated when an uploaded file checksum does not
68    /// match the expected checksum.
69    #[error("file upload checksum mismatch; expected '{0}' but got '{1}'")]
70    FileChecksumMismatch(String, String),
71
72    /// Error generated by the protocol library.
73    #[error(transparent)]
74    Protocol(#[from] sos_protocol::Error),
75
76    /// Error generated by the core library.
77    #[error(transparent)]
78    Core(#[from] sos_core::Error),
79
80    /// Error generated by the backend library.
81    #[error(transparent)]
82    Backend(#[from] sos_backend::Error),
83
84    /// Error generated by the signer library.
85    #[error(transparent)]
86    Signer(#[from] sos_signer::Error),
87
88    /// Error generated by the storage library.
89    #[error(transparent)]
90    Storage(#[from] sos_server_storage::Error),
91
92    /// Error generated by the backend storage.
93    #[error(transparent)]
94    BackendStorage(#[from] sos_backend::StorageError),
95
96    /// Error generated by the database library.
97    #[error(transparent)]
98    Database(#[from] sos_database::Error),
99
100    /// Error generated converting from a slice.
101    #[error(transparent)]
102    TryFromSlice(#[from] std::array::TryFromSliceError),
103
104    /// Error generated attempting to parse a URL.
105    #[error(transparent)]
106    Url(#[from] url::ParseError),
107
108    /// Error generated when a header value is invalid.
109    #[error(transparent)]
110    HeaderValue(#[from] axum::http::header::InvalidHeaderValue),
111
112    /// Error generated by the web server library.
113    #[error(transparent)]
114    WebServer(#[from] axum::Error),
115
116    /// Error generated by the io module.
117    #[error(transparent)]
118    Io(#[from] std::io::Error),
119
120    /// Error generated deserializing from TOML.
121    #[error(transparent)]
122    TomlDeser(#[from] toml::de::Error),
123
124    /// Error generated serializing to TOML.
125    #[error(transparent)]
126    TomlSer(#[from] toml::ser::Error),
127
128    /// Error generated attempting to parse a socket address.
129    #[error(transparent)]
130    AddrParse(#[from] std::net::AddrParseError),
131
132    /// Error generate by the ECDSA library.
133    #[error(transparent)]
134    Ecdsa(#[from] k256::ecdsa::Error),
135
136    /// Error generate by the JSON library.
137    #[error(transparent)]
138    SerdeJson(#[from] serde_json::Error),
139
140    /// Error generate by the UUID library.
141    #[error(transparent)]
142    Uuid(#[from] uuid::Error),
143
144    /// Error generated trying to decode from base58.
145    #[error(transparent)]
146    Base58(#[from] bs58::decode::Error),
147
148    /// Error generated by the HTTP library.
149    #[error(transparent)]
150    Http(#[from] axum::http::Error),
151
152    /// Error generated by the HTTP library.
153    #[error(transparent)]
154    Uri(#[from] http::uri::InvalidUri),
155}
156
157impl Error {
158    /// Status code for the error.
159    pub fn status(&self) -> StatusCode {
160        match self {
161            Self::Status(status) => *status,
162            Self::Json(status, _) => *status,
163            Self::NoAccount(_) => StatusCode::NOT_FOUND,
164            Self::Unauthorized => StatusCode::UNAUTHORIZED,
165            Self::BadRequest => StatusCode::BAD_REQUEST,
166            Self::Forbidden => StatusCode::FORBIDDEN,
167            Self::Conflict => StatusCode::CONFLICT,
168            Self::BackendStorage(
169                sos_backend::StorageError::FolderNotFound(_),
170            ) => StatusCode::NOT_FOUND,
171            _ => StatusCode::INTERNAL_SERVER_ERROR,
172        }
173    }
174
175    /// Get the body for the error response.
176    pub fn body(self) -> Value {
177        match self {
178            Self::Status(status) => {
179                let message =
180                    status.canonical_reason().unwrap_or("unknown reason");
181                let status: u16 = status.into();
182                json!({ "code": status, "message": message })
183            }
184            Self::Json(status, value) => match status {
185                StatusCode::OK => value,
186                _ => {
187                    let status: u16 = status.into();
188                    json!({ "code": status, "message": value })
189                }
190            },
191            _ => {
192                let status: u16 = self.status().into();
193                let message = self.to_string();
194                json!({ "code": status, "message": message })
195            }
196        }
197    }
198}
199
200impl IntoResponse for Error {
201    fn into_response(self) -> Response<Body> {
202        let status = self.status();
203        let body = self.body();
204        (status, Json(body)).into_response()
205    }
206}