1use 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#[derive(Debug, Error)]
14pub enum Error {
15 #[error("{0}")]
17 Status(StatusCode),
18
19 #[error("{0} {1}")]
21 Json(StatusCode, Value),
22
23 #[error("unauthorized, may need to retry the protocol handshake")]
25 Unauthorized,
26
27 #[error("bad request")]
29 BadRequest,
30
31 #[error("forbidden")]
33 Forbidden,
34
35 #[error("conflict")]
37 Conflict,
38
39 #[error("unknown rpc method '{0}'")]
41 RpcUnknownMethod(String),
42
43 #[error("path {0} is not a file")]
45 NotFile(PathBuf),
46
47 #[error("not a directory {0}")]
49 NotDirectory(PathBuf),
50
51 #[error("directory {0} already exists")]
53 DirectoryExists(PathBuf),
54
55 #[error("file {0} already exists")]
57 FileExists(PathBuf),
58
59 #[error("account '{0}' does not exist")]
61 NoAccount(AccountId),
62
63 #[error("account '{0}' already exists")]
65 AccountExists(AccountId),
66
67 #[error("file upload checksum mismatch; expected '{0}' but got '{1}'")]
70 FileChecksumMismatch(String, String),
71
72 #[error(transparent)]
74 Protocol(#[from] sos_protocol::Error),
75
76 #[error(transparent)]
78 Core(#[from] sos_core::Error),
79
80 #[error(transparent)]
82 Backend(#[from] sos_backend::Error),
83
84 #[error(transparent)]
86 Signer(#[from] sos_signer::Error),
87
88 #[error(transparent)]
90 Storage(#[from] sos_server_storage::Error),
91
92 #[error(transparent)]
94 BackendStorage(#[from] sos_backend::StorageError),
95
96 #[error(transparent)]
98 Database(#[from] sos_database::Error),
99
100 #[error(transparent)]
102 TryFromSlice(#[from] std::array::TryFromSliceError),
103
104 #[error(transparent)]
106 Url(#[from] url::ParseError),
107
108 #[error(transparent)]
110 HeaderValue(#[from] axum::http::header::InvalidHeaderValue),
111
112 #[error(transparent)]
114 WebServer(#[from] axum::Error),
115
116 #[error(transparent)]
118 Io(#[from] std::io::Error),
119
120 #[error(transparent)]
122 TomlDeser(#[from] toml::de::Error),
123
124 #[error(transparent)]
126 TomlSer(#[from] toml::ser::Error),
127
128 #[error(transparent)]
130 AddrParse(#[from] std::net::AddrParseError),
131
132 #[error(transparent)]
134 Ecdsa(#[from] k256::ecdsa::Error),
135
136 #[error(transparent)]
138 SerdeJson(#[from] serde_json::Error),
139
140 #[error(transparent)]
142 Uuid(#[from] uuid::Error),
143
144 #[error(transparent)]
146 Base58(#[from] bs58::decode::Error),
147
148 #[error(transparent)]
150 Http(#[from] axum::http::Error),
151
152 #[error(transparent)]
154 Uri(#[from] http::uri::InvalidUri),
155}
156
157impl Error {
158 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 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}