1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use std::fmt;
use hyper::Error as HyperError;
use hyper::error::ParseError;
use std::error::Error as StdError;
use std::io;
use std::collections::HashMap;
use serde_json;
use serde_json::Error as JsonError;

use self::Error::{Json, Http, Io, Query, Url};

#[derive(Debug)]
pub enum Error {
    Json(serde_json::Error),
    Http(HyperError),
    Url(ParseError),
    Io(io::Error),
    Query { error: String, description: String },
}

impl Error {
    pub fn from_couch(msg: &str) -> Error {
        let convert = match serde_json::from_str::<HashMap<String, String>>(msg) {
            Ok(map) => map,
            Err(e) => return Json(e),
        };

        let err = match convert.get("error") {
            Some(msg) => msg.to_string(),
            None => "Unknown".to_string(),
        };

        let description = match convert.get("reason") {
            Some(msg) => msg.to_string(),
            None => "unknown reason".to_string(),
        };

        Query {
            error: err,
            description: description,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Json(ref e) => write!(f, "{}", e),
            Http(ref e) => write!(f, "{}", e),
            Io(ref e) => write!(f, "{}", e),
            Url(ref e) => write!(f, "{}", e),
            Query { ref error, ref description } => write!(f, "{}:{}", error, description),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Json(ref e) => e.description(),
            Http(ref e) => e.description(),
            Io(ref e) => e.description(),
            Url(ref e) => e.description(),
            Query { .. } => "CouchDB responded with an error",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Json(ref e) => Some(e),
            Http(ref e) => Some(e),
            Io(ref e) => Some(e),
            Url(ref e) => Some(e),
            Query { .. } => None,
        }
    }
}


impl From<HyperError> for Error {
    fn from(err: HyperError) -> Error {
        Http(err)
    }
}

impl From<ParseError> for Error {
    fn from(err: ParseError) -> Error {
        Url(err)
    }
}

impl From<JsonError> for Error {
    fn from(err: JsonError) -> Error {
        Json(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Io(err)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_couch_warns_on_json_parse_error() {
        match Error::from_couch("This will not parse") {
            Json(_) => assert!(true),
            _ => panic!("I was expecting a Json error!"),
        }
    }

    #[test]
    fn from_couch_returns_parsed_json_error() {
        let str = "{\"error\":\"not_found\",\"reason\":\"Database does not exist.\"}";
        match Error::from_couch(str) {
            Query { ref error, .. } => assert_eq!(error, "not_found"),
            _ => panic!("I was expecting query error"),

        }
    }

    #[test]
    fn from_couch_works_with_different_hashmap_values() {
        let str = "{\"oops\":\"not_found\",\"what_happened\":\"Database does not exist.\"}";
        match Error::from_couch(str) {
            Query { ref error, ref description } => {
                assert_eq!(error, "Unknown");
                assert_eq!(description, "unknown reason");
            }
            _ => panic!("I was expecting query error"),
        }
    }
}