use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use snafu::Snafu;
#[derive(Debug, Serialize, Deserialize, Snafu, JsonSchema)]
#[snafu(visibility = "pub(crate)")]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum StdError {
#[snafu(display("Generic error: {}", msg))]
GenericErr {
msg: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Invalid Base64 string: {}", msg))]
InvalidBase64 {
msg: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Cannot decode UTF8 bytes into string: {}", msg))]
InvalidUtf8 {
msg: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("{} not found", kind))]
NotFound {
kind: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Received null pointer, refuse to use"))]
NullPointer {
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Error parsing into type {}: {}", target, msg))]
ParseErr {
target: String,
msg: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Error serializing type {}: {}", source, msg))]
SerializeErr {
#[snafu(source(false))]
source: String,
msg: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Unauthorized"))]
Unauthorized {
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Cannot subtract {} from {}", subtrahend, minuend))]
Underflow {
minuend: String,
subtrahend: String,
#[serde(skip)]
backtrace: Option<snafu::Backtrace>,
},
}
impl PartialEq for StdError {
#[allow(clippy::unneeded_field_pattern)]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
StdError::GenericErr { msg, backtrace: _ },
StdError::GenericErr {
msg: msg2,
backtrace: _,
},
) => msg == msg2,
(
StdError::InvalidBase64 { msg, backtrace: _ },
StdError::InvalidBase64 {
msg: msg2,
backtrace: _,
},
) => msg == msg2,
(
StdError::InvalidUtf8 { msg, backtrace: _ },
StdError::InvalidUtf8 {
msg: msg2,
backtrace: _,
},
) => msg == msg2,
(
StdError::NotFound { kind, backtrace: _ },
StdError::NotFound {
kind: kind2,
backtrace: _,
},
) => kind == kind2,
(StdError::NullPointer { backtrace: _ }, StdError::NullPointer { backtrace: _ }) => {
true
}
(
StdError::ParseErr {
target,
msg,
backtrace: _,
},
StdError::ParseErr {
target: target2,
msg: msg2,
backtrace: _,
},
) => target == target2 && msg == msg2,
(
StdError::SerializeErr {
source,
msg,
backtrace: _,
},
StdError::SerializeErr {
source: source2,
msg: msg2,
backtrace: _,
},
) => source == source2 && msg == msg2,
(StdError::Unauthorized { backtrace: _ }, StdError::Unauthorized { backtrace: _ }) => {
true
}
(
StdError::Underflow {
minuend,
subtrahend,
backtrace: _,
},
StdError::Underflow {
minuend: minued2,
subtrahend: subtrahend2,
backtrace: _,
},
) => minuend == minued2 && subtrahend == subtrahend2,
_ => false,
}
}
}
pub type StdResult<T> = core::result::Result<T, StdError>;
#[cfg(test)]
mod test {
use super::*;
use crate::serde::{from_slice, to_vec};
#[test]
fn can_serialize() {
let error = InvalidBase64 {
msg: "invalid length".to_string(),
}
.build();
assert_eq!(
to_vec(&error).unwrap(),
br#"{"invalid_base64":{"msg":"invalid length"}}"#.to_vec()
);
}
#[test]
fn can_deserialize() {
let error: StdError =
from_slice(br#"{"invalid_base64":{"msg":"invalid length"}}"#).unwrap();
match error {
StdError::InvalidBase64 { msg, backtrace } => {
assert_eq!(msg, "invalid length");
assert!(backtrace.is_none());
}
_ => panic!("invalid type"),
};
}
#[test]
fn can_deserialize_from_non_static_source() {
let source = (br#"{"not_found":{"kind":"bugs"}}"#).to_vec();
let error: StdError = from_slice(&source).unwrap();
match error {
StdError::NotFound { kind, backtrace } => {
assert_eq!(kind, "bugs");
assert!(backtrace.is_none());
}
_ => panic!("invalid type"),
};
}
#[test]
fn eq_works() {
let error1 = StdError::InvalidBase64 {
msg: "invalid length".to_string(),
backtrace: None,
};
let error2 = StdError::InvalidBase64 {
msg: "invalid length".to_string(),
backtrace: None,
};
assert_eq!(error1, error2);
}
#[test]
fn ne_works() {
let error1 = StdError::InvalidBase64 {
msg: "invalid length".to_string(),
backtrace: None,
};
let error2 = StdError::InvalidBase64 {
msg: "other bla".to_string(),
backtrace: None,
};
assert_ne!(error1, error2);
}
fn assert_conversion(original: StdError) {
let seralized = to_vec(&original).unwrap();
let restored: StdError = from_slice(&seralized).unwrap();
assert_eq!(restored, original);
}
#[test]
fn generic_err_conversion() {
assert_conversion(GenericErr { msg: "something" }.build());
}
#[test]
fn invalid_base64_conversion() {
assert_conversion(
InvalidBase64 {
msg: "invalid length".to_string(),
}
.build(),
);
}
#[test]
fn unauthorized_conversion() {
assert_conversion(Unauthorized {}.build());
}
#[test]
fn null_pointer_conversion() {
assert_conversion(NullPointer {}.build());
}
#[test]
fn not_found_conversion() {
assert_conversion(NotFound { kind: "State" }.build());
}
#[test]
fn parse_err_conversion() {
let err = from_slice::<String>(b"123").unwrap_err();
assert_conversion(err);
}
#[test]
fn serialize_err_conversion() {
assert_conversion(
SerializeErr {
source: "Person",
msg: "buffer is full",
}
.build(),
);
}
}