use crate::{
core::PubNubError,
lib::{
alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec::Vec,
},
collections::HashMap,
},
};
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct APISuccessBody<D> {
pub status: i32,
pub data: D,
pub service: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct APISuccessBodyWithPayload<D> {
pub status: i32,
pub message: String,
pub payload: D,
pub service: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct APISuccessBodyWithMessage {
pub status: i32,
pub message: String,
pub service: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct APISuccessBodyWithFlattenedPayload<D> {
pub status: i32,
pub message: String,
#[cfg_attr(feature = "serde", serde(flatten))]
pub payload: D,
pub service: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum APIErrorBody {
AsArray2(u8, String),
AsArray3(u8, String, Option<String>),
AsObjectWithServiceAndErrorPayload {
status: u16,
error: bool,
service: String,
message: String,
payload: ErrorObjectPayload,
},
AsObjectWithService {
status: u16,
error: bool,
service: String,
message: String,
},
AsObjectWithMessage {
status: u16,
error: bool,
message: String,
},
AsObjectWithErrorMessageAndChannels {
status: u16,
error: bool,
error_message: String,
channels: Option<HashMap<String, String>>,
},
AsObjectWithErrorMessage {
status: u16,
error: bool,
error_message: String,
},
AsObjectWithErrorObjectDetails {
status: u16,
service: String,
error: ErrorObjectWithDetails,
},
AsObjectWithErrorObject {
status: u16,
error: ErrorObject,
},
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ErrorObjectPayload {
channels: Option<Vec<String>>,
#[cfg_attr(feature = "serde", serde(rename = "channel-groups"))]
channel_groups: Option<Vec<String>>,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct ErrorObjectDetails {
message: String,
location: String,
#[cfg_attr(feature = "serde", serde(rename(deserialize = "locationType")))]
location_type: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ErrorObject {
message: String,
source: String,
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ErrorObjectWithDetails {
message: String,
source: String,
details: Vec<ErrorObjectDetails>,
}
impl APIErrorBody {
fn status(&self) -> u16 {
match self {
APIErrorBody::AsObjectWithServiceAndErrorPayload { status, .. } => *status,
APIErrorBody::AsObjectWithService { status, .. } => *status,
APIErrorBody::AsObjectWithMessage { status, .. } => *status,
APIErrorBody::AsObjectWithErrorMessageAndChannels { status, .. } => *status,
APIErrorBody::AsObjectWithErrorMessage { status, .. } => *status,
APIErrorBody::AsObjectWithErrorObjectDetails { status, .. } => *status,
APIErrorBody::AsObjectWithErrorObject { status, .. } => *status,
APIErrorBody::AsArray2(_, _) => 400,
APIErrorBody::AsArray3(_, _, _) => 400,
}
}
fn service(&self) -> Option<String> {
match self {
APIErrorBody::AsObjectWithServiceAndErrorPayload { service, .. } => {
Some(service.to_owned())
}
APIErrorBody::AsObjectWithErrorObjectDetails { service, .. } => {
Some(service.to_owned())
}
APIErrorBody::AsObjectWithService { service, .. } => Some(service.to_owned()),
_ => None,
}
}
fn message(&self) -> String {
match self {
APIErrorBody::AsArray2(_, message) => message.to_owned(),
APIErrorBody::AsArray3(_, message, _) => message.to_owned(),
APIErrorBody::AsObjectWithServiceAndErrorPayload {
message, payload, ..
} => {
let mut affected: Vec<String> = Vec::new();
if let Some(channels) = &payload.channels {
if !channels.is_empty() {
affected.push(format!("affected channels: {}", channels.join(", ")))
}
}
if let Some(groups) = &payload.channel_groups {
if !groups.is_empty() {
affected.push(format!("affected channel groups: {}", groups.join(", ")))
}
}
if affected.is_empty() {
message.to_string()
} else {
message.to_string() + &format!(" ({})", affected.join("; "))
}
}
APIErrorBody::AsObjectWithService { message, .. } => message.to_owned(),
APIErrorBody::AsObjectWithMessage { message, .. } => message.to_owned(),
APIErrorBody::AsObjectWithErrorMessageAndChannels { error_message, .. } => {
error_message.to_owned()
}
APIErrorBody::AsObjectWithErrorMessage { error_message, .. } => {
error_message.to_owned()
}
APIErrorBody::AsObjectWithErrorObjectDetails { error, .. } => {
let mut message = format!("{}\nDetails:\n", error.message);
error.details.iter().for_each(|detail| {
message += format!(
" * {} (location: '{}', name: '{}')\n",
detail.message, detail.location_type, detail.location
)
.as_str();
});
message.trim().to_string()
}
APIErrorBody::AsObjectWithErrorObject { error, .. } => error.message.to_string(),
}
}
fn affected_channels(&self) -> Option<Vec<String>> {
match self {
APIErrorBody::AsObjectWithServiceAndErrorPayload { payload, .. } => {
payload.channels.clone()
}
_ => None,
}
}
fn affected_channel_groups(&self) -> Option<Vec<String>> {
match &self {
APIErrorBody::AsObjectWithServiceAndErrorPayload { payload, .. } => {
payload.channel_groups.clone()
}
_ => None,
}
}
}
impl From<APIErrorBody> for PubNubError {
fn from(value: APIErrorBody) -> Self {
PubNubError::API {
status: value.status(),
message: value.message(),
service: value.service(),
affected_channels: value.affected_channels(),
affected_channel_groups: value.affected_channel_groups(),
response: None,
}
}
}