use std::fmt::Display;
use std::str::FromStr;
use peanuts::element::{FromElement, IntoElement};
use peanuts::{DeserializeError, Element};
use thiserror::Error;
use crate::stanza_error::Error as StanzaError;
use crate::stanza_error::Text;
use super::XMLNS;
#[derive(Clone, Debug, Error)]
pub struct Error {
by: Option<String>,
r#type: ErrorType,
// TODO: children (sequence)
errors: Vec<StanzaError>,
text: Option<Text>,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{},", self.r#type)?;
for error in &self.errors {
write!(f, "{}: ", error)?;
}
if let Some(text) = &self.text {
if let Some(text) = &text.text {
write!(f, ": {}", text)?;
}
}
if let Some(by) = &self.by {
write!(f, " (error returned by `{}`)", by)?;
}
Ok(())
}
}
impl FromElement for Error {
fn from_element(mut element: peanuts::Element) -> peanuts::element::DeserializeResult<Self> {
element.check_name("error")?;
element.check_name(XMLNS)?;
let by = element.attribute_opt("by")?;
let r#type = element.attribute("type")?;
let errors = element.children()?;
let text = element.child_opt()?;
Ok(Error {
by,
r#type,
errors,
text,
})
}
}
impl IntoElement for Error {
fn builder(&self) -> peanuts::element::ElementBuilder {
Element::builder("error", Some(XMLNS))
.push_attribute_opt("by", self.by.clone())
.push_attribute("type", self.r#type)
.push_children(self.errors.clone())
.push_child_opt(self.text.clone())
}
}
#[derive(Copy, Clone, Debug)]
pub enum ErrorType {
Auth,
Cancel,
Continue,
Modify,
Wait,
}
impl Display for ErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorType::Auth => f.write_str("auth"),
ErrorType::Cancel => f.write_str("cancel"),
ErrorType::Continue => f.write_str("continue"),
ErrorType::Modify => f.write_str("modify"),
ErrorType::Wait => f.write_str("wait"),
}
}
}
impl FromStr for ErrorType {
type Err = DeserializeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auth" => Ok(ErrorType::Auth),
"cancel" => Ok(ErrorType::Cancel),
"continue" => Ok(ErrorType::Continue),
"modify" => Ok(ErrorType::Modify),
"wait" => Ok(ErrorType::Wait),
_ => Err(DeserializeError::FromStr(s.to_string())),
}
}
}
impl From<StanzaError> for Error {
fn from(value: StanzaError) -> Self {
let error_type = match value {
StanzaError::BadRequest => ErrorType::Modify,
StanzaError::Conflict => ErrorType::Cancel,
// cancel or modify
StanzaError::FeatureNotImplemented => ErrorType::Cancel,
StanzaError::Forbidden => ErrorType::Auth,
StanzaError::Gone(_) => ErrorType::Cancel,
StanzaError::InternalServerError => ErrorType::Cancel,
StanzaError::ItemNotFound => ErrorType::Cancel,
StanzaError::JIDMalformed => ErrorType::Modify,
StanzaError::NotAcceptable => ErrorType::Modify,
StanzaError::NotAllowed => ErrorType::Cancel,
StanzaError::NotAuthorized => ErrorType::Auth,
// modify or wait
StanzaError::PolicyViolation => ErrorType::Modify,
StanzaError::RecipientUnavailable => ErrorType::Wait,
StanzaError::Redirect(_) => ErrorType::Modify,
StanzaError::RegistrationRequired => ErrorType::Auth,
StanzaError::RemoteServerNotFound => ErrorType::Cancel,
StanzaError::RemoteServerTimeout => ErrorType::Wait,
StanzaError::ResourceConstraint => ErrorType::Wait,
StanzaError::ServiceUnavailable => ErrorType::Cancel,
StanzaError::SubscriptionRequired => ErrorType::Auth,
StanzaError::UndefinedCondition => ErrorType::Cancel,
// wait or modify
StanzaError::UnexpectedRequest => ErrorType::Modify,
#[cfg(feature = "xep_0060")]
StanzaError::PubSub(ref error) => error.r#type(),
};
Self {
by: None,
r#type: error_type,
errors: value.into(),
text: None,
}
}
}
impl From<StanzaError> for Vec<StanzaError> {
fn from(value: StanzaError) -> Self {
match value {
#[cfg(feature = "xep_0060")]
StanzaError::PubSub(error) => error.stanza_errors(),
_ => vec![value],
}
}
}