From 81fc578ca655e9b63b1368337865fb8bf4c88701 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 22 May 2015 19:51:44 -0700 Subject: [PATCH] Move ugh_privacy stuff to normal places Use private traits to expose constructors internally --- src/error.rs | 182 +++++++++++++++++++++++++++++++++++++- src/lib.rs | 45 ++++++---- src/types/mod.rs | 39 +++++++- src/ugh_privacy.rs | 215 --------------------------------------------- 4 files changed, 242 insertions(+), 239 deletions(-) delete mode 100644 src/ugh_privacy.rs diff --git a/src/error.rs b/src/error.rs index 62a5b778..1761c9ee 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,17 +1,193 @@ -pub use ugh_privacy::DbError; - 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; +use {Result, DbErrorNew}; use types::Type; include!(concat!(env!("OUT_DIR"), "/sqlstate.rs")); +/// A Postgres error or notice. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct DbError { + severity: String, + code: SqlState, + message: String, + detail: Option, + hint: Option, + position: Option, + where_: Option, + schema: Option, + table: Option, + column: Option, + datatype: Option, + constraint: Option, + file: String, + line: u32, + routine: String, +} + +impl DbErrorNew for DbError { + fn new_raw(fields: Vec<(u8, String)>) -> result::Result { + 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(())), + }) + } + + fn new_connect(fields: Vec<(u8, String)>) -> result::Result { + match DbError::new_raw(fields) { + Ok(err) => Err(ConnectError::DbError(err)), + Err(()) => Err(ConnectError::BadResponse), + } + } + + fn new(fields: Vec<(u8, String)>) -> Result { + match DbError::new_raw(fields) { + Ok(err) => Err(Error::DbError(err)), + Err(()) => Err(Error::BadResponse), + } + } +} + +impl DbError { + /// The field contents are ERROR, FATAL, or PANIC (in an error message), + /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a + /// localized translation of one of these. + pub fn severity(&self) -> &str { + &self.severity + } + + /// The SQLSTATE code for the error. + pub fn code(&self) -> &SqlState { + &self.code + } + + /// The primary human-readable error message. This should be accurate but + /// terse (typically one line). + pub fn message(&self) -> &str { + &self.message + } + + /// An optional secondary error message carrying more detail about the + /// problem. Might run to multiple lines. + pub fn detail(&self) -> Option<&str> { + self.detail.as_ref().map(|s| &**s) + } + + /// An optional suggestion what to do about the problem. This is intended + /// to differ from Detail in that it offers advice (potentially + /// inappropriate) rather than hard facts. Might run to multiple lines. + pub fn hint(&self) -> Option<&str> { + self.hint.as_ref().map(|s| &**s) + } + + /// An optional error cursor position into either the original query string + /// or an internally generated query. + pub fn position(&self) -> Option<&ErrorPosition> { + self.position.as_ref() + } + + /// An indication of the context in which the error occurred. Presently + /// this includes a call stack traceback of active procedural language + /// functions and internally-generated queries. The trace is one entry per + /// line, most recent first. + pub fn where_(&self) -> Option<&str> { + self.where_.as_ref().map(|s| &**s) + } + + /// If the error was associated with a specific database object, the name + /// of the schema containing that object, if any. (PostgreSQL 9.3+) + pub fn schema(&self) -> Option<&str> { + self.schema.as_ref().map(|s| &**s) + } + + /// If the error was associated with a specific table, the name of the + /// table. (Refer to the schema name field for the name of the table's + /// schema.) (PostgreSQL 9.3+) + pub fn table(&self) -> Option<&str> { + self.table.as_ref().map(|s| &**s) + } + + /// If the error was associated with a specific table column, the name of + /// the column. (Refer to the schema and table name fields to identify the + /// table.) (PostgreSQL 9.3+) + pub fn column(&self) -> Option<&str> { + self.column.as_ref().map(|s| &**s) + } + + /// If the error was associated with a specific data type, the name of the + /// data type. (Refer to the schema name field for the name of the data + /// type's schema.) (PostgreSQL 9.3+) + pub fn datatype(&self) -> Option<&str> { + self.datatype.as_ref().map(|s| &**s) + } + + /// If the error was associated with a specific constraint, the name of the + /// constraint. Refer to fields listed above for the associated table or + /// domain. (For this purpose, indexes are treated as constraints, even if + /// they weren't created with constraint syntax.) (PostgreSQL 9.3+) + pub fn constraint(&self) -> Option<&str> { + self.constraint.as_ref().map(|s| &**s) + } + + /// The file name of the source-code location where the error was reported. + pub fn file(&self) -> &str { + &self.file + } + + /// The line number of the source-code location where the error was + /// reported. + pub fn line(&self) -> u32 { + self.line + } + + /// The name of the source-code routine reporting the error. + pub fn routine(&self) -> &str { + &self.routine + } +} + +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 + } +} + /// Reasons a new Postgres connection could fail. #[derive(Debug)] pub enum ConnectError { diff --git a/src/lib.rs b/src/lib.rs index 8a5f4869..3f7302d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,7 @@ use std::path::PathBuf; pub use error::{Error, ConnectError, SqlState, DbError, ErrorPosition}; #[doc(inline)] -pub use types::{Oid, Type, Kind, ToSql, FromSql, SessionInfo}; +pub use types::{Oid, Type, Other, Kind, ToSql, FromSql, SessionInfo}; use io::{StreamWrapper, NegotiateSsl}; use types::IsNull; #[doc(inline)] @@ -95,7 +95,6 @@ mod macros; mod error; pub mod io; mod message; -mod ugh_privacy; mod priv_io; mod url; mod util; @@ -548,7 +547,7 @@ impl InnerConnection { conn.cancel_data.secret_key = secret_key; } ReadyForQuery { .. } => break, - ErrorResponse { fields } => return ugh_privacy::dberror_new_connect(fields), + ErrorResponse { fields } => return DbError::new_connect(fields), _ => return Err(ConnectError::BadResponse), } } @@ -596,7 +595,7 @@ impl InnerConnection { debug_assert!(!self.desynchronized); match try_desync!(self, self.stream.read_message()) { NoticeResponse { fields } => { - if let Ok(err) = ugh_privacy::dberror_new_raw(fields) { + if let Ok(err) = DbError::new_raw(fields) { self.notice_handler.handle_notice(err); } Ok(None) @@ -659,13 +658,13 @@ impl InnerConnection { | AuthenticationSCMCredential | AuthenticationGSS | AuthenticationSSPI => return Err(ConnectError::UnsupportedAuthentication), - ErrorResponse { fields } => return ugh_privacy::dberror_new_connect(fields), + ErrorResponse { fields } => return DbError::new_connect(fields), _ => return Err(ConnectError::BadResponse) } match try!(self.read_message()) { AuthenticationOk => Ok(()), - ErrorResponse { fields } => return ugh_privacy::dberror_new_connect(fields), + ErrorResponse { fields } => return DbError::new_connect(fields), _ => return Err(ConnectError::BadResponse) } } @@ -693,7 +692,7 @@ impl InnerConnection { ParseComplete => {} ErrorResponse { fields } => { try!(self.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => bad_response!(self), } @@ -812,7 +811,7 @@ impl InnerConnection { Sync])); let resp = match try!(self.read_message()) { CloseComplete => Ok(()), - ErrorResponse { fields } => ugh_privacy::dberror_new(fields), + ErrorResponse { fields } => DbError::new(fields), _ => bad_response!(self) }; try!(self.wait_for_ready()); @@ -851,7 +850,7 @@ impl InnerConnection { BindComplete => {} ErrorResponse { fields } => { try!(self.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => bad_response!(self) } @@ -871,7 +870,7 @@ impl InnerConnection { } ErrorResponse { fields } => { try!(self.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => bad_response!(self) }; @@ -879,7 +878,7 @@ impl InnerConnection { CommandComplete { .. } => {} ErrorResponse { fields } => { try!(self.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => bad_response!(self) } @@ -894,7 +893,7 @@ impl InnerConnection { } }; - let type_ = Type::Other(Box::new(ugh_privacy::new_other(name, oid, kind))); + let type_ = Type::Other(Box::new(Other::new(name, oid, kind))); self.unknown_types.insert(oid, type_.clone()); Ok(type_) } @@ -933,7 +932,7 @@ impl InnerConnection { } ErrorResponse { fields } => { try!(self.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => {} } @@ -1466,7 +1465,7 @@ impl<'conn> Statement<'conn> { BindComplete => Ok(()), ErrorResponse { fields } => { try!(conn.wait_for_ready()); - ugh_privacy::dberror_new(fields) + DbError::new(fields) } _ => { conn.desynchronized = true; @@ -1527,7 +1526,7 @@ impl<'conn> Statement<'conn> { DataRow { .. } => {} ErrorResponse { fields } => { try!(conn.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } CommandComplete { tag } => { num = util::parse_update_count(tag); @@ -1683,7 +1682,7 @@ fn read_rows(conn: &mut InnerConnection, buf: &mut VecDeque>> DataRow { row } => buf.push_back(row), ErrorResponse { fields } => { try!(conn.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } CopyInResponse { .. } => { try!(conn.write_messages(&[ @@ -2133,7 +2132,7 @@ impl<'a> CopyInStatement<'a> { BindComplete => {}, ErrorResponse { fields } => { try!(conn.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => { conn.desynchronized = true; @@ -2210,7 +2209,7 @@ impl<'a> CopyInStatement<'a> { CommandComplete { tag } => util::parse_update_count(tag), ErrorResponse { fields } => { try!(conn.wait_for_ready()); - return ugh_privacy::dberror_new(fields); + return DbError::new(fields); } _ => { conn.desynchronized = true; @@ -2319,3 +2318,13 @@ impl<'a> GenericConnection for Transaction<'a> { self.is_active() } } + +trait OtherNew { + fn new(name: String, oid: Oid, kind: Kind) -> Other; +} + +trait DbErrorNew { + fn new_raw(fields: Vec<(u8, String)>) -> result::Result; + fn new_connect(fields: Vec<(u8, String)>) -> result::Result; + fn new(fields: Vec<(u8, String)>) -> Result; +} diff --git a/src/types/mod.rs b/src/types/mod.rs index fc14e4cf..44953d6b 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,12 +6,10 @@ use std::fmt; use std::io::prelude::*; use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; -use {Result, SessionInfoNew, InnerConnection}; +use {Result, SessionInfoNew, InnerConnection, OtherNew}; use error::Error; use util; -pub use ugh_privacy::Other; - /// Generates a simple implementation of `ToSql::accepts` which accepts the /// types passed to it. #[macro_export] @@ -481,6 +479,41 @@ make_postgres_type! { 3838 => EventTrigger: Kind::Simple } +/// Information about an unknown type. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Other { + name: String, + oid: Oid, + kind: Kind, +} + +impl OtherNew for Other { + fn new(name: String, oid: Oid, kind: Kind) -> Other { + Other { + name: name, + oid: oid, + kind: kind, + } + } +} + +impl Other { + /// The name of the type. + pub fn name(&self) -> &str { + &self.name + } + + /// The OID of this type. + pub fn oid(&self) -> Oid { + self.oid + } + + /// The kind of this type. + pub fn kind(&self) -> &Kind { + &self.kind + } +} + /// A trait for types that can be created from a Postgres value. /// /// # Types diff --git a/src/ugh_privacy.rs b/src/ugh_privacy.rs deleted file mode 100644 index cf5d67f4..00000000 --- a/src/ugh_privacy.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::collections::HashMap; -use std::error; -use std::fmt; -use std::result; - -use Result; -use types::{Oid, Kind}; -use error::{SqlState, ErrorPosition, ConnectError, Error}; - -/// Information about an unknown type. -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct Other { - name: String, - oid: Oid, - kind: Kind, -} - -pub fn new_other(name: String, oid: Oid, kind: Kind) -> Other { - Other { - name: name, - oid: oid, - kind: kind, - } -} - -impl Other { - /// The name of the type. - pub fn name(&self) -> &str { - &self.name - } - - /// The OID of this type. - pub fn oid(&self) -> Oid { - self.oid - } - - /// The kind of this type. - pub fn kind(&self) -> &Kind { - &self.kind - } -} - -/// A Postgres error or notice. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct DbError { - severity: String, - code: SqlState, - message: String, - detail: Option, - hint: Option, - position: Option, - where_: Option, - schema: Option, - table: Option, - column: Option, - datatype: Option, - constraint: Option, - file: String, - line: u32, - routine: String, -} - -pub fn dberror_new_raw(fields: Vec<(u8, String)>) -> result::Result { - 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(())), - }) -} - -pub fn dberror_new_connect(fields: Vec<(u8, String)>) -> result::Result { - match dberror_new_raw(fields) { - Ok(err) => Err(ConnectError::DbError(err)), - Err(()) => Err(ConnectError::BadResponse), - } -} - -pub fn dberror_new(fields: Vec<(u8, String)>) -> Result { - match dberror_new_raw(fields) { - Ok(err) => Err(Error::DbError(err)), - Err(()) => Err(Error::BadResponse), - } -} - -impl DbError { - /// The field contents are ERROR, FATAL, or PANIC (in an error message), - /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a - /// localized translation of one of these. - pub fn severity(&self) -> &str { - &self.severity - } - - /// The SQLSTATE code for the error. - pub fn code(&self) -> &SqlState { - &self.code - } - - /// The primary human-readable error message. This should be accurate but - /// terse (typically one line). - pub fn message(&self) -> &str { - &self.message - } - - /// An optional secondary error message carrying more detail about the - /// problem. Might run to multiple lines. - pub fn detail(&self) -> Option<&str> { - self.detail.as_ref().map(|s| &**s) - } - - /// An optional suggestion what to do about the problem. This is intended - /// to differ from Detail in that it offers advice (potentially - /// inappropriate) rather than hard facts. Might run to multiple lines. - pub fn hint(&self) -> Option<&str> { - self.hint.as_ref().map(|s| &**s) - } - - /// An optional error cursor position into either the original query string - /// or an internally generated query. - pub fn position(&self) -> Option<&ErrorPosition> { - self.position.as_ref() - } - - /// An indication of the context in which the error occurred. Presently - /// this includes a call stack traceback of active procedural language - /// functions and internally-generated queries. The trace is one entry per - /// line, most recent first. - pub fn where_(&self) -> Option<&str> { - self.where_.as_ref().map(|s| &**s) - } - - /// If the error was associated with a specific database object, the name - /// of the schema containing that object, if any. (PostgreSQL 9.3+) - pub fn schema(&self) -> Option<&str> { - self.schema.as_ref().map(|s| &**s) - } - - /// If the error was associated with a specific table, the name of the - /// table. (Refer to the schema name field for the name of the table's - /// schema.) (PostgreSQL 9.3+) - pub fn table(&self) -> Option<&str> { - self.table.as_ref().map(|s| &**s) - } - - /// If the error was associated with a specific table column, the name of - /// the column. (Refer to the schema and table name fields to identify the - /// table.) (PostgreSQL 9.3+) - pub fn column(&self) -> Option<&str> { - self.column.as_ref().map(|s| &**s) - } - - /// If the error was associated with a specific data type, the name of the - /// data type. (Refer to the schema name field for the name of the data - /// type's schema.) (PostgreSQL 9.3+) - pub fn datatype(&self) -> Option<&str> { - self.datatype.as_ref().map(|s| &**s) - } - - /// If the error was associated with a specific constraint, the name of the - /// constraint. Refer to fields listed above for the associated table or - /// domain. (For this purpose, indexes are treated as constraints, even if - /// they weren't created with constraint syntax.) (PostgreSQL 9.3+) - pub fn constraint(&self) -> Option<&str> { - self.constraint.as_ref().map(|s| &**s) - } - - /// The file name of the source-code location where the error was reported. - pub fn file(&self) -> &str { - &self.file - } - - /// The line number of the source-code location where the error was - /// reported. - pub fn line(&self) -> u32 { - self.line - } - - /// The name of the source-code routine reporting the error. - pub fn routine(&self) -> &str { - &self.routine - } -} - -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 - } -}