use std::convert::Infallible;
use std::error::Error;
use std::fmt::{self, Display};
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
#[serde(transparent)]
pub struct ApiErrorId(pub String);
impl FromStr for ApiErrorId {
type Err = Infallible;
fn from_str(s: &str) -> Result<ApiErrorId, Infallible> {
Ok(ApiErrorId(s.to_string()))
}
}
impl Display for ApiErrorId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub enum ApiErrorKind {
Client,
Server,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ApiError {
pub id: ApiErrorId,
pub message: String,
pub code: String,
pub kind: ApiErrorKind,
#[serde(default)]
pub info: serde_json::Value,
}
impl Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ApiErrorKind::Client => write!(f, "Client error: ")?,
ApiErrorKind::Server => write!(f, "Server error: ")?,
}
write!(f, "{} ({})", self.message, self.code)
}
}
impl Error for ApiError {}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
#[must_use = "this `ApiResult` may be an `Err` variant, which should be handled"]
pub enum ApiResult<T> {
Err {
error: ApiError,
},
Ok(T),
}
impl<T> Into<Result<T, ApiError>> for ApiResult<T> {
fn into(self) -> Result<T, ApiError> {
self.into_result()
}
}
impl<T> ApiResult<T> {
pub fn into_result(self) -> Result<T, ApiError> {
match self {
ApiResult::Err { error } => Err(error),
ApiResult::Ok(x) => Ok(x),
}
}
pub fn ok(self) -> Option<T> {
match self {
ApiResult::Err { .. } => None,
ApiResult::Ok(x) => Some(x),
}
}
pub fn err(self) -> Option<ApiError> {
match self {
ApiResult::Err { error } => Some(error),
ApiResult::Ok(_) => None,
}
}
pub fn is_ok(&self) -> bool {
match self {
ApiResult::Err { .. } => false,
ApiResult::Ok(_) => true,
}
}
pub fn is_err(&self) -> bool {
!self.is_ok()
}
pub fn expect(self, msg: &str) -> T {
match self {
ApiResult::Err { error } => panic!("{}: {:?}", msg, error),
ApiResult::Ok(x) => x,
}
}
pub fn unwrap(self) -> T {
self.expect("called `ApiResult::unwrap()` on an `ApiResult::Err` value")
}
pub fn map<U, F>(self, op: F) -> ApiResult<U>
where
F: FnOnce(T) -> U,
{
match self {
ApiResult::Ok(x) => ApiResult::Ok(op(x)),
ApiResult::Err { error } => ApiResult::Err { error },
}
}
}