aboutsummaryrefslogblamecommitdiffstats
path: root/stanza/src/client/error.rs
blob: 33bc85e94583f95ca9251261ec68350567ad3d0d (plain) (tree)
1
2
3
4
5
6
7
8
9
                      



                                                 
                     
 

                                              


                 
                              







                          















                                                                        





































                                                                                                 











                                                                        













                                                               





































                                                                    
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,
    // children (sequence)
    error: StanzaError,
    text: Option<Text>,
}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}, {}", self.r#type, self.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 error = element.pop_child_one()?;
        let text = element.pop_child_opt()?;

        Ok(Error {
            by,
            r#type,
            error,
            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_child(self.error.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,
        };
        Self {
            by: None,
            r#type: error_type,
            error: value,
            text: None,
        }
    }
}