use crate::sdk::signer::ecdsa::Address;
use axum::{
body::Body,
http::StatusCode,
response::{IntoResponse, Json, Response},
};
use serde_json::{json, Value};
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("{0}")]
Status(StatusCode),
#[error("{0} {1}")]
Json(StatusCode, Value),
#[error("unauthorized, may need to retry the protocol handshake")]
Unauthorized,
#[error("bad request")]
BadRequest,
#[error("forbidden")]
Forbidden,
#[error("conflict")]
Conflict,
#[error("unknown rpc method '{0}'")]
RpcUnknownMethod(String),
#[error("path {0} is not a file")]
NotFile(PathBuf),
#[error("not a directory {0}")]
NotDirectory(PathBuf),
#[error("directory {0} already exists")]
DirectoryExists(PathBuf),
#[error("file {0} already exists")]
FileExists(PathBuf),
#[error("account '{0}' does not exist")]
NoAccount(Address),
#[error("account '{0}' already exists")]
AccountExists(Address),
#[error("file upload checksum mismatch; expected '{0}' but got '{1}'")]
FileChecksumMismatch(String, String),
#[error(transparent)]
Net(#[from] crate::Error),
#[error(transparent)]
TryFromSlice(#[from] std::array::TryFromSliceError),
#[error(transparent)]
Core(#[from] sos_sdk::Error),
#[error(transparent)]
Url(#[from] url::ParseError),
#[error(transparent)]
HeaderValue(#[from] axum::http::header::InvalidHeaderValue),
#[error(transparent)]
WebServer(#[from] axum::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
TomlDeser(#[from] toml::de::Error),
#[error(transparent)]
TomlSer(#[from] toml::ser::Error),
#[error(transparent)]
AddrParse(#[from] std::net::AddrParseError),
#[error(transparent)]
Ecdsa(#[from] sos_sdk::k256::ecdsa::Error),
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),
#[error(transparent)]
Uuid(#[from] uuid::Error),
#[error(transparent)]
Base58(#[from] bs58::decode::Error),
#[error(transparent)]
Http(#[from] axum::http::Error),
}
impl Error {
pub fn status(&self) -> StatusCode {
match self {
Self::Status(status) => *status,
Self::Json(status, _) => *status,
Self::NoAccount(_) => StatusCode::NOT_FOUND,
Self::Unauthorized => StatusCode::UNAUTHORIZED,
Self::BadRequest => StatusCode::BAD_REQUEST,
Self::Forbidden => StatusCode::FORBIDDEN,
Self::Conflict => StatusCode::CONFLICT,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
pub fn body(self) -> Value {
match self {
Self::Status(status) => {
let message =
status.canonical_reason().unwrap_or("unknown reason");
let status: u16 = status.into();
json!({ "code": status, "message": message })
}
Self::Json(status, value) => match status {
StatusCode::OK => value,
_ => {
let status: u16 = status.into();
json!({ "code": status, "message": value })
}
},
_ => {
let status: u16 = self.status().into();
let message = self.to_string();
json!({ "code": status, "message": message })
}
}
}
}
impl IntoResponse for Error {
fn into_response(self) -> Response<Body> {
let status = self.status();
let body = self.body();
(status, Json(body)).into_response()
}
}