use std::error; use std::fmt; use std::borrow::Cow; use std::ffi::CStr; use std::ffi::NulError; use bson::{de,ser,Document}; use bson::document::ValueAccessError; use crate::mongoc::bindings; /// Wrapper for all errors that can occur in the driver. pub enum MongoError { /// Error in the underlying C driver. Bsonc(BsoncError), /// Error decoding Bson. Decoder(de::Error), /// Error encoding Bson. Encoder(ser::Error), /// Error accessing a value on a Bson document. ValueAccessError(ValueAccessError), /// Invalid params error that can be reported by the underlying C driver. InvalidParams(InvalidParamsError), // from CString::new(db) Nul(NulError) } impl fmt::Display for MongoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { MongoError::Bsonc(ref err) => write!(f, "{}", err), MongoError::Encoder(ref err) => write!(f, "{}", err), MongoError::Decoder(ref err) => write!(f, "{}", err), MongoError::ValueAccessError(ref err) => write!(f, "{}", err), MongoError::InvalidParams(ref err) => write!(f, "{}", err), MongoError::Nul(ref err) => write!(f, "{}", err) } } } impl fmt::Debug for MongoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { MongoError::Bsonc(ref err) => write!(f, "MongoError ({:?})", err), MongoError::Decoder(ref err) => write!(f, "MongoError ({:?})", err), MongoError::Encoder(ref err) => write!(f, "MongoError ({:?})", err), MongoError::ValueAccessError(ref err) => write!(f, "MongoError ({:?})", err), MongoError::InvalidParams(ref err) => write!(f, "MongoError ({:?})", err), MongoError::Nul(ref err) => write!(f, "MongoError ({:?})", err) } } } impl error::Error for MongoError { fn cause(&self) -> Option<&dyn error::Error> { match *self { MongoError::Bsonc(ref err) => Some(err), MongoError::Decoder(ref err) => Some(err), MongoError::Encoder(ref err) => Some(err), MongoError::ValueAccessError(ref err) => Some(err), MongoError::InvalidParams(ref err) => Some(err), MongoError::Nul(ref err) => Some(err) } } } impl From for MongoError { fn from(error: de::Error) -> MongoError { MongoError::Decoder(error) } } impl From for MongoError { fn from(error: ser::Error) -> MongoError { MongoError::Encoder(error) } } impl From for MongoError { fn from(error: ValueAccessError) -> MongoError { MongoError::ValueAccessError(error) } } impl From for MongoError { fn from(error: NulError) -> MongoError { MongoError::Nul(error) } } /// Error in the underlying C driver. pub struct BsoncError { inner: bindings::bson_error_t, } /// MongoDB error domain. #[derive(Debug,PartialEq)] pub enum MongoErrorDomain { Blank, Client, Stream, Protocol, Cursor, Query, Insert, Sasl, Bson, Matcher, Namespace, Command, Collection, Gridfs, Scram, Unknown } /// MongoDB error code. #[derive(Debug,PartialEq)] pub enum MongoErrorCode { Blank, StreamInvalidType, StreamInvalidState, StreamNameResolution, StreamSocket, StreamConnect, StreamNotEstablished, ClientNotReady, ClientTooBig, ClientTooSmall, ClientGetnonce, ClientAuthenticate, ClientNoAcceptablePeer, ClientInExhaust, ProtocolInvalidReply, ProtocolBadWireVersion, CursorInvalidCursor, QueryFailure, BsonInvalid, MatcherInvalid, NamespaceInvalid, NamespaceInvalidFilterType, CommandInvalidArg, CollectionInsertFailed, CollectionUpdateFailed, CollectionDeleteFailed, CollectionDoesNotExist, GridfsInvalidFilename, ScramNotDone, ScramProtocolError, QueryCommandNotFound, QueryNotTailable, WriteConcernError, DuplicateKey, Unknown(u32) } impl BsoncError { pub fn empty() -> BsoncError { BsoncError { inner: bindings::bson_error_t { domain: 0, code: 0, message: [0; 504] } } } /// Wether the error has content. pub fn is_empty(&self) -> bool { self.inner.domain == 0 && self.inner.code == 0 } /// The error's domain. pub fn domain(&self) -> MongoErrorDomain { match self.inner.domain { 0 => MongoErrorDomain::Blank, bindings::MONGOC_ERROR_CLIENT => MongoErrorDomain::Client, bindings::MONGOC_ERROR_STREAM => MongoErrorDomain::Stream, bindings::MONGOC_ERROR_PROTOCOL => MongoErrorDomain::Protocol, bindings::MONGOC_ERROR_CURSOR => MongoErrorDomain::Cursor, bindings::MONGOC_ERROR_QUERY => MongoErrorDomain::Query, bindings::MONGOC_ERROR_INSERT => MongoErrorDomain::Insert, bindings::MONGOC_ERROR_SASL => MongoErrorDomain::Sasl, bindings::MONGOC_ERROR_BSON => MongoErrorDomain::Bson, bindings::MONGOC_ERROR_MATCHER => MongoErrorDomain::Matcher, bindings::MONGOC_ERROR_NAMESPACE => MongoErrorDomain::Namespace, bindings::MONGOC_ERROR_COMMAND => MongoErrorDomain::Command, bindings::MONGOC_ERROR_COLLECTION => MongoErrorDomain::Collection, bindings::MONGOC_ERROR_GRIDFS => MongoErrorDomain::Gridfs, bindings::MONGOC_ERROR_SCRAM => MongoErrorDomain::Scram, _ => MongoErrorDomain::Unknown } } /// The error's code. pub fn code(&self) -> MongoErrorCode { match self.inner.code { 0 => MongoErrorCode::Blank, bindings::MONGOC_ERROR_STREAM_INVALID_TYPE => MongoErrorCode::StreamInvalidType, bindings::MONGOC_ERROR_STREAM_INVALID_STATE => MongoErrorCode::StreamInvalidState, bindings::MONGOC_ERROR_STREAM_NAME_RESOLUTION => MongoErrorCode::StreamNameResolution, bindings::MONGOC_ERROR_STREAM_SOCKET => MongoErrorCode::StreamSocket, bindings::MONGOC_ERROR_STREAM_CONNECT => MongoErrorCode::StreamConnect, bindings::MONGOC_ERROR_STREAM_NOT_ESTABLISHED => MongoErrorCode::StreamNotEstablished, bindings::MONGOC_ERROR_CLIENT_NOT_READY => MongoErrorCode::ClientNotReady, bindings::MONGOC_ERROR_CLIENT_TOO_BIG => MongoErrorCode::ClientTooBig, bindings::MONGOC_ERROR_CLIENT_TOO_SMALL => MongoErrorCode::ClientTooSmall, bindings::MONGOC_ERROR_CLIENT_GETNONCE => MongoErrorCode::ClientGetnonce, bindings::MONGOC_ERROR_CLIENT_AUTHENTICATE => MongoErrorCode::ClientAuthenticate, bindings::MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER => MongoErrorCode::ClientNoAcceptablePeer, bindings::MONGOC_ERROR_CLIENT_IN_EXHAUST => MongoErrorCode::ClientInExhaust, bindings::MONGOC_ERROR_PROTOCOL_INVALID_REPLY => MongoErrorCode::ProtocolInvalidReply, bindings::MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION => MongoErrorCode::ProtocolBadWireVersion, bindings::MONGOC_ERROR_CURSOR_INVALID_CURSOR => MongoErrorCode::CursorInvalidCursor, bindings::MONGOC_ERROR_QUERY_FAILURE => MongoErrorCode::QueryFailure, bindings::MONGOC_ERROR_BSON_INVALID => MongoErrorCode::BsonInvalid, bindings::MONGOC_ERROR_MATCHER_INVALID => MongoErrorCode::MatcherInvalid, bindings::MONGOC_ERROR_NAMESPACE_INVALID => MongoErrorCode::NamespaceInvalid, bindings::MONGOC_ERROR_NAMESPACE_INVALID_FILTER_TYPE => MongoErrorCode::NamespaceInvalidFilterType, bindings::MONGOC_ERROR_COMMAND_INVALID_ARG => MongoErrorCode::CommandInvalidArg, bindings::MONGOC_ERROR_COLLECTION_INSERT_FAILED => MongoErrorCode::CollectionInsertFailed, bindings::MONGOC_ERROR_COLLECTION_UPDATE_FAILED => MongoErrorCode::CollectionUpdateFailed, bindings::MONGOC_ERROR_COLLECTION_DELETE_FAILED => MongoErrorCode::CollectionDeleteFailed, bindings::MONGOC_ERROR_COLLECTION_DOES_NOT_EXIST => MongoErrorCode::CollectionDoesNotExist, bindings::MONGOC_ERROR_GRIDFS_INVALID_FILENAME => MongoErrorCode::GridfsInvalidFilename, bindings::MONGOC_ERROR_SCRAM_NOT_DONE => MongoErrorCode::ScramNotDone, bindings::MONGOC_ERROR_SCRAM_PROTOCOL_ERROR => MongoErrorCode::ScramProtocolError, bindings::MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND => MongoErrorCode::QueryCommandNotFound, bindings::MONGOC_ERROR_QUERY_NOT_TAILABLE => MongoErrorCode::QueryNotTailable, bindings::MONGOC_ERROR_WRITE_CONCERN_ERROR => MongoErrorCode::WriteConcernError, bindings::MONGOC_ERROR_DUPLICATE_KEY => MongoErrorCode::DuplicateKey, code => MongoErrorCode::Unknown(code) } } /// The error's message. pub fn get_message(&self) -> Cow { let cstr = unsafe { CStr::from_ptr(&self.inner.message as *const i8) }; String::from_utf8_lossy(cstr.to_bytes()) } #[doc(hidden)] pub fn mut_inner(&mut self) -> &mut bindings::bson_error_t { &mut self.inner } } impl fmt::Debug for BsoncError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "BsoncError: {:?}/{:?} - {}", &self.domain(), &self.code(), &self.get_message() ) } } impl fmt::Display for BsoncError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.get_message()) } } impl error::Error for BsoncError { fn description(&self) -> &str { "Error reported by the underlying Mongo C driver" } } impl From for MongoError { fn from(error: BsoncError) -> MongoError { MongoError::Bsonc(error) } } /// Invalid params error that can be reported by the underlying C driver. pub struct InvalidParamsError; impl fmt::Debug for InvalidParamsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "InvalidParamsError: Invalid params supplied") } } impl fmt::Display for InvalidParamsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Invalid params supplied") } } impl error::Error for InvalidParamsError { fn description(&self) -> &str { "Invalid params reported by the underlying Mongo C driver, no more information is available" } } impl From for MongoError { fn from(error: InvalidParamsError) -> MongoError { MongoError::InvalidParams(error) } } /// Error returned by a bulk operation that includes a report in the reply document. #[derive(Debug)] pub struct BulkOperationError { /// Returned error pub error: MongoError, /// Error report pub reply: Document } impl fmt::Display for BulkOperationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Bulk operation error {}", self.error) } } impl error::Error for BulkOperationError { fn description(&self) -> &str { "Error returned by a bulk operation that includes a report in the reply document" } } #[cfg(test)] mod tests { use super::{BsoncError,MongoErrorDomain,MongoErrorCode}; #[test] fn test_bson_error_empty() { let mut error = BsoncError::empty(); assert!(error.is_empty()); error.mut_inner().code = 1; assert!(!error.is_empty()); error.mut_inner().domain = 1; error.mut_inner().code = 0; assert!(!error.is_empty()); } #[test] fn test_bson_error_domain() { let mut error = BsoncError::empty(); assert_eq!(MongoErrorDomain::Blank, error.domain()); error.mut_inner().domain = 1; assert_eq!(MongoErrorDomain::Client, error.domain()); } #[test] fn test_bson_error_code() { let mut error = BsoncError::empty(); assert_eq!(MongoErrorCode::Blank, error.code()); error.mut_inner().code = 1; assert_eq!(MongoErrorCode::StreamInvalidType, error.code()); } }