use Value;
use xml::common::TextPosition;
use xml::reader::Error as XmlError;
use std::collections::BTreeMap;
use std::fmt::{self, Display, Formatter};
use std::{error, io};
#[derive(Debug)]
pub struct Error(RequestErrorKind);
impl Error {
pub fn fault(&self) -> Option<&Fault> {
match self.0 {
RequestErrorKind::Fault(ref fault) => Some(fault),
_ => None,
}
}
}
#[doc(hidden)] impl From<RequestErrorKind> for Error {
fn from(kind: RequestErrorKind) -> Self {
Error(kind)
}
}
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.0.source()
}
}
#[derive(Debug)]
pub enum RequestErrorKind {
ParseError(ParseError),
TransportError(Box<dyn error::Error + Send + Sync>),
Fault(Fault),
}
impl From<ParseError> for RequestErrorKind {
fn from(e: ParseError) -> Self {
RequestErrorKind::ParseError(e)
}
}
impl From<Fault> for RequestErrorKind {
fn from(f: Fault) -> Self {
RequestErrorKind::Fault(f)
}
}
impl Display for RequestErrorKind {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
RequestErrorKind::ParseError(ref err) => write!(fmt, "parse error: {}", err),
RequestErrorKind::TransportError(ref err) => write!(fmt, "transport error: {}", err),
RequestErrorKind::Fault(ref err) => write!(fmt, "{}", err),
}
}
}
impl error::Error for RequestErrorKind {
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
RequestErrorKind::ParseError(ref err) => Some(err),
RequestErrorKind::TransportError(ref err) => Some(err.as_ref()),
RequestErrorKind::Fault(ref err) => Some(err),
}
}
}
#[derive(Debug, PartialEq)]
pub enum ParseError {
XmlError(XmlError),
InvalidValue {
for_type: &'static str,
found: String,
position: TextPosition,
},
UnexpectedXml {
expected: String,
found: Option<String>,
position: TextPosition,
},
}
impl From<XmlError> for ParseError {
fn from(e: XmlError) -> Self {
ParseError::XmlError(e)
}
}
impl From<io::Error> for ParseError {
fn from(e: io::Error) -> Self {
ParseError::XmlError(XmlError::from(e))
}
}
impl Display for ParseError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
ParseError::XmlError(ref err) => write!(fmt, "malformed XML: {}", err),
ParseError::InvalidValue {
for_type,
ref found,
ref position,
} => write!(
fmt,
"invalid value for type '{}' at {}: {}",
for_type, position, found
),
ParseError::UnexpectedXml {
ref expected,
ref position,
found: None,
} => write!(
fmt,
"unexpected XML at {} (expected {})",
position, expected
),
ParseError::UnexpectedXml {
ref expected,
ref position,
found: Some(ref found),
} => write!(
fmt,
"unexpected XML at {} (expected {}, found {})",
position, expected, found
),
}
}
}
impl error::Error for ParseError {}
#[derive(Debug, PartialEq, Eq)]
pub struct Fault {
pub fault_code: i32,
pub fault_string: String,
}
impl Fault {
pub fn from_value(value: &Value) -> Option<Self> {
match *value {
Value::Struct(ref map) => {
if map.len() != 2 {
return None;
}
match (map.get("faultCode"), map.get("faultString")) {
(Some(&Value::Int(fault_code)), Some(&Value::String(ref fault_string))) => {
Some(Fault {
fault_code,
fault_string: fault_string.to_string(),
})
}
_ => None,
}
}
_ => None,
}
}
pub fn to_value(&self) -> Value {
let mut map = BTreeMap::new();
map.insert("faultCode".to_string(), Value::from(self.fault_code));
map.insert(
"faultString".to_string(),
Value::from(self.fault_string.as_ref()),
);
Value::Struct(map)
}
}
impl Display for Fault {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.fault_string, self.fault_code)
}
}
impl error::Error for Fault {}
#[cfg(test)]
mod tests {
use super::*;
use std::error;
#[test]
fn fault_roundtrip() {
let input = Fault {
fault_code: -123456,
fault_string: "The Bald Lazy House Jumps Over The Hyperactive Kitten".to_string(),
};
assert_eq!(Fault::from_value(&input.to_value()), Some(input));
}
#[test]
fn error_impls_error() {
fn assert_error<T: error::Error>() {}
assert_error::<Error>();
}
#[test]
fn error_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Error>();
}
}