use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use crate::error::Error;
use crate::result::Result;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, Zeroize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ErrorCode {
BadRequest,
InvalidApiKey,
RequestVariableMissing,
UserDeactivated,
RealmDeactivated,
RateLimitHit,
StreamDoesNotExist,
BadNarrow,
}
impl ErrorCode {
pub const BAD_REQUEST: &str = "BAD_REQUEST";
pub const INVALID_API_KEY: &str = "INVALID_API_KEY";
pub const REQUEST_VARIABLE_MISSING: &str = "REQUEST_VARIABLE_MISSING";
pub const USER_DEACTIVATED: &str = "USER_DEACTIVATED";
pub const REALM_DEACTIVATED: &str = "REALM_DEACTIVATED";
pub const RATE_LIMIT_HIT: &str = "RATE_LIMIT_HIT";
pub const STREAM_DOES_NOT_EXIST: &str = "STREAM_DOES_NOT_EXIST";
pub const BAD_NARROW: &str = "BAD_NARROW";
pub const fn new() -> Self {
Self::BadRequest
}
pub const fn as_str(&self) -> &'static str {
match self {
Self::BadRequest => Self::BAD_REQUEST,
Self::InvalidApiKey => Self::INVALID_API_KEY,
Self::RequestVariableMissing => Self::REQUEST_VARIABLE_MISSING,
Self::UserDeactivated => Self::USER_DEACTIVATED,
Self::RealmDeactivated => Self::REALM_DEACTIVATED,
Self::RateLimitHit => Self::RATE_LIMIT_HIT,
Self::StreamDoesNotExist => Self::STREAM_DOES_NOT_EXIST,
Self::BadNarrow => Self::BAD_NARROW,
}
}
pub fn try_from_str<S: Into<String>>(val: S) -> Result<Self> {
match val.into().to_uppercase().as_str() {
Self::BAD_REQUEST => Ok(Self::BadRequest),
Self::INVALID_API_KEY => Ok(Self::InvalidApiKey),
Self::REQUEST_VARIABLE_MISSING => Ok(Self::RequestVariableMissing),
Self::USER_DEACTIVATED => Ok(Self::UserDeactivated),
Self::REALM_DEACTIVATED => Ok(Self::RealmDeactivated),
Self::RATE_LIMIT_HIT => Ok(Self::RateLimitHit),
Self::STREAM_DOES_NOT_EXIST => Ok(Self::StreamDoesNotExist),
Self::BAD_NARROW => Ok(Self::BadNarrow),
s => Err(Error::io_other(format!("invalid ErrorCode: {s}"))),
}
}
}
impl Default for ErrorCode {
fn default() -> Self {
Self::new()
}
}
impl From<ErrorCode> for &'static str {
fn from(val: ErrorCode) -> Self {
val.as_str()
}
}
impl From<ErrorCode> for String {
fn from(val: ErrorCode) -> Self {
val.as_str().to_string()
}
}
impl TryFrom<&str> for ErrorCode {
type Error = Error;
fn try_from(val: &str) -> Result<Self> {
Self::try_from_str(val)
}
}
impl TryFrom<String> for ErrorCode {
type Error = Error;
fn try_from(val: String) -> Result<Self> {
Self::try_from_str(val)
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_code() {
[
(ErrorCode::BadRequest, ErrorCode::BAD_REQUEST),
(ErrorCode::InvalidApiKey, ErrorCode::INVALID_API_KEY),
(
ErrorCode::RequestVariableMissing,
ErrorCode::REQUEST_VARIABLE_MISSING,
),
(ErrorCode::UserDeactivated, ErrorCode::USER_DEACTIVATED),
(ErrorCode::RealmDeactivated, ErrorCode::REALM_DEACTIVATED),
(ErrorCode::RateLimitHit, ErrorCode::RATE_LIMIT_HIT),
(
ErrorCode::StreamDoesNotExist,
ErrorCode::STREAM_DOES_NOT_EXIST,
),
(ErrorCode::BadNarrow, ErrorCode::BAD_NARROW),
]
.into_iter()
.for_each(|(code, code_str)| {
let code_json = format!(r#""{code}""#);
assert_eq!(code.as_str(), code_str);
assert_eq!(<&str>::from(code), code_str);
assert_eq!(String::from(code), code_str.to_string());
assert_eq!(ErrorCode::try_from_str(code_str), Ok(code));
assert_eq!(ErrorCode::try_from(code_str), Ok(code));
assert_eq!(ErrorCode::try_from(code_str.to_string()), Ok(code));
assert_eq!(serde_json::to_string(&code).unwrap(), code_json);
assert_eq!(serde_json::from_str::<ErrorCode>(&code_json).unwrap(), code);
});
}
}