use byteorder;
use phf;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;
use std::collections::HashMap;
use {Result, DbErrorNew};
include!(concat!(env!("OUT_DIR"), "/sqlstate.rs"));
#[derive(Clone, PartialEq, Eq)]
pub struct DbError {
pub severity: String,
pub code: SqlState,
pub message: String,
pub detail: Option<String>,
pub hint: Option<String>,
pub position: Option<ErrorPosition>,
pub where_: Option<String>,
pub schema: Option<String>,
pub table: Option<String>,
pub column: Option<String>,
pub datatype: Option<String>,
pub constraint: Option<String>,
pub file: String,
pub line: u32,
pub routine: String,
_p: (),
}
impl DbErrorNew for DbError {
fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
let mut map: HashMap<_, _> = fields.into_iter().collect();
Ok(DbError {
severity: try!(map.remove(&b'S').ok_or(())),
code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
message: try!(map.remove(&b'M').ok_or(())),
detail: map.remove(&b'D'),
hint: map.remove(&b'H'),
position: match map.remove(&b'P') {
Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
None => {
match map.remove(&b'p') {
Some(pos) => {
Some(ErrorPosition::Internal {
position: try!(pos.parse().map_err(|_| ())),
query: try!(map.remove(&b'q').ok_or(())),
})
}
None => None,
}
}
},
where_: map.remove(&b'W'),
schema: map.remove(&b's'),
table: map.remove(&b't'),
column: map.remove(&b'c'),
datatype: map.remove(&b'd'),
constraint: map.remove(&b'n'),
file: try!(map.remove(&b'F').ok_or(())),
line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())),
routine: try!(map.remove(&b'R').ok_or(())),
_p: (),
})
}
fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
match DbError::new_raw(fields) {
Ok(err) => Err(ConnectError::Db(Box::new(err))),
Err(()) => Err(ConnectError::Io(::bad_response())),
}
}
fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
match DbError::new_raw(fields) {
Ok(err) => Err(Error::Db(Box::new(err))),
Err(()) => Err(Error::Io(::bad_response())),
}
}
}
impl fmt::Debug for DbError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("DbError")
.field("severity", &self.severity)
.field("code", &self.code)
.field("message", &self.message)
.field("detail", &self.detail)
.field("hint", &self.hint)
.field("position", &self.position)
.field("where_", &self.where_)
.field("schema", &self.schema)
.field("table", &self.table)
.field("column", &self.column)
.field("datatype", &self.datatype)
.field("constraint", &self.constraint)
.field("file", &self.file)
.field("line", &self.line)
.field("routine", &self.routine)
.finish()
}
}
impl fmt::Display for DbError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}: {}", self.severity, self.message)
}
}
impl error::Error for DbError {
fn description(&self) -> &str {
&self.message
}
}
#[derive(Debug)]
pub enum ConnectError {
ConnectParams(Box<error::Error + Sync + Send>),
Db(Box<DbError>),
Ssl(Box<error::Error + Sync + Send>),
Io(io::Error),
}
impl fmt::Display for ConnectError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(fmt.write_str(error::Error::description(self)));
match *self {
ConnectError::ConnectParams(ref msg) => write!(fmt, ": {}", msg),
ConnectError::Db(ref err) => write!(fmt, ": {}", err),
ConnectError::Ssl(ref err) => write!(fmt, ": {}", err),
ConnectError::Io(ref err) => write!(fmt, ": {}", err),
}
}
}
impl error::Error for ConnectError {
fn description(&self) -> &str {
match *self {
ConnectError::ConnectParams(_) => "Invalid connection parameters",
ConnectError::Db(_) => "Error reported by Postgres",
ConnectError::Ssl(_) => "Error initiating SSL session",
ConnectError::Io(_) => "Error communicating with the server",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
ConnectError::ConnectParams(ref err) => Some(&**err),
ConnectError::Db(ref err) => Some(&**err),
ConnectError::Ssl(ref err) => Some(&**err),
ConnectError::Io(ref err) => Some(err),
}
}
}
impl From<io::Error> for ConnectError {
fn from(err: io::Error) -> ConnectError {
ConnectError::Io(err)
}
}
impl From<DbError> for ConnectError {
fn from(err: DbError) -> ConnectError {
ConnectError::Db(Box::new(err))
}
}
impl From<byteorder::Error> for ConnectError {
fn from(err: byteorder::Error) -> ConnectError {
ConnectError::Io(From::from(err))
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
Normal(u32),
Internal {
position: u32,
query: String,
},
}
#[derive(Debug)]
pub enum Error {
Db(Box<DbError>),
Io(io::Error),
Conversion(Box<error::Error + Sync + Send>),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(fmt.write_str(error::Error::description(self)));
match *self {
Error::Db(ref err) => write!(fmt, ": {}", err),
Error::Io(ref err) => write!(fmt, ": {}", err),
Error::Conversion(ref err) => write!(fmt, ": {}", err),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::Db(_) => "Error reported by Postgres",
Error::Io(_) => "Error communicating with the server",
Error::Conversion(_) => "Error converting between Postgres and Rust types",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::Db(ref err) => Some(&**err),
Error::Io(ref err) => Some(err),
Error::Conversion(ref err) => Some(&**err),
}
}
}
impl From<DbError> for Error {
fn from(err: DbError) -> Error {
Error::Db(Box::new(err))
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<byteorder::Error> for Error {
fn from(err: byteorder::Error) -> Error {
Error::Io(From::from(err))
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
}