diff options
author | Ashley Mannix <ashleymannix@live.com.au> | 2017-05-28 19:20:10 +1000 |
---|---|---|
committer | Paul Masurel <paul.masurel@gmail.com> | 2017-05-29 18:29:39 +0900 |
commit | 1bcebdd29e7366e3b9d3179e2b08775ea4edc880 (patch) | |
tree | c39a04082b205d5714889eb289d71ce1f59be9f1 /src | |
parent | ed0333a404ba300b80d95594ec4132aedfa31116 (diff) |
initial error-chain
Diffstat (limited to 'src')
-rw-r--r-- | src/core/index.rs | 4 | ||||
-rw-r--r-- | src/directory/error.rs | 59 | ||||
-rw-r--r-- | src/directory/managed_directory.rs | 9 | ||||
-rw-r--r-- | src/directory/mmap_directory.rs | 40 | ||||
-rw-r--r-- | src/directory/ram_directory.rs | 11 | ||||
-rw-r--r-- | src/error.rs | 108 | ||||
-rw-r--r-- | src/indexer/index_writer.rs | 20 | ||||
-rw-r--r-- | src/indexer/merger.rs | 4 | ||||
-rw-r--r-- | src/indexer/segment_updater.rs | 8 | ||||
-rw-r--r-- | src/lib.rs | 3 |
10 files changed, 171 insertions, 95 deletions
diff --git a/src/core/index.rs b/src/core/index.rs index e43cfc7..34453ad 100644 --- a/src/core/index.rs +++ b/src/core/index.rs @@ -1,5 +1,5 @@ use Result; -use Error; +use error::*; use serde_json; use schema::Schema; use std::sync::Arc; @@ -30,7 +30,7 @@ fn load_metas(directory: &Directory) -> Result<IndexMeta> { let meta_data = directory.atomic_read(&META_FILEPATH)?; let meta_string = String::from_utf8_lossy(&meta_data); serde_json::from_str(&meta_string) - .map_err(|e| Error::CorruptedFile(META_FILEPATH.clone(), Box::new(e))) + .chain_err(|| ErrorKind::CorruptedFile(META_FILEPATH.clone())) } /// Tantivy's Search Index diff --git a/src/directory/error.rs b/src/directory/error.rs index 2bc2b6f..c3d2ece 100644 --- a/src/directory/error.rs +++ b/src/directory/error.rs @@ -1,5 +1,43 @@ use std::path::PathBuf; use std::io; +use std::fmt; + +/// General IO error with an optional path to the offending file. +#[derive(Debug)] +pub struct IOError { + path: Option<PathBuf>, + err: io::Error, +} + +impl fmt::Display for IOError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.path { + Some(ref path) => write!(f, "io error occurred on path '{:?}': '{}'", path, self.err), + None => write!(f, "io error occurred: '{}'", self.err), + } + } +} + +impl IOError { + pub(crate) fn with_path( + path: PathBuf, + err: io::Error) + -> Self { + IOError { + path: Some(path), + err: err, + } + } +} + +impl From<io::Error> for IOError { + fn from(err: io::Error) -> IOError { + IOError { + path: None, + err: err, + } + } +} /// Error that may occur when opening a directory #[derive(Debug)] @@ -18,11 +56,11 @@ pub enum OpenWriteError { FileAlreadyExists(PathBuf), /// Any kind of IO error that happens when /// writing in the underlying IO device. - IOError(io::Error), + IOError(IOError), } -impl From<io::Error> for OpenWriteError { - fn from(err: io::Error) -> OpenWriteError { +impl From<IOError> for OpenWriteError { + fn from(err: IOError) -> OpenWriteError { OpenWriteError::IOError(err) } } @@ -34,9 +72,14 @@ pub enum OpenReadError { FileDoesNotExist(PathBuf), /// Any kind of IO error that happens when /// interacting with the underlying IO device. - IOError(io::Error), + IOError(IOError), } +impl From<IOError> for OpenReadError { + fn from(err: IOError) -> OpenReadError { + OpenReadError::IOError(err) + } +} /// Error that may occur when trying to delete a file #[derive(Debug)] @@ -45,8 +88,14 @@ pub enum DeleteError { FileDoesNotExist(PathBuf), /// Any kind of IO error that happens when /// interacting with the underlying IO device. - IOError(io::Error), + IOError(IOError), /// The file may not be deleted because it is /// protected. FileProtected(PathBuf), } + +impl From<IOError> for DeleteError { + fn from(err: IOError) -> DeleteError { + DeleteError::IOError(err) + } +} diff --git a/src/directory/managed_directory.rs b/src/directory/managed_directory.rs index 7b87bdd..e957bd0 100644 --- a/src/directory/managed_directory.rs +++ b/src/directory/managed_directory.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; use serde_json; -use directory::error::{OpenReadError, DeleteError, OpenWriteError}; +use directory::error::{IOError, OpenReadError, DeleteError, OpenWriteError}; use directory::{ReadOnlySource, WritePtr}; use std::result; use std::io; @@ -12,8 +12,7 @@ use std::io::Write; use core::MANAGED_FILEPATH; use std::collections::HashMap; use std::fmt; -use Result; -use Error; +use error::*; /// Wrapper of directories that keeps track of files created by Tantivy. /// @@ -86,7 +85,7 @@ impl ManagedDirectory { let managed_files_json = String::from_utf8_lossy(&data); let managed_files: HashSet<PathBuf> = serde_json::from_str(&managed_files_json) - .map_err(|e| Error::CorruptedFile(MANAGED_FILEPATH.clone(), Box::new(e)))?; + .chain_err(|| ErrorKind::CorruptedFile(MANAGED_FILEPATH.clone()))?; Ok(ManagedDirectory { directory: box directory, meta_informations: Arc::new(RwLock::new(MetaInformation { @@ -230,7 +229,7 @@ impl Directory for ManagedDirectory { } fn open_write(&mut self, path: &Path) -> result::Result<WritePtr, OpenWriteError> { - self.register_file_as_managed(path)?; + self.register_file_as_managed(path).map_err(|e| IOError::with_path(path.to_owned(), e))?; self.directory.open_write(path) } diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs index f01c813..6e48a00 100644 --- a/src/directory/mmap_directory.rs +++ b/src/directory/mmap_directory.rs @@ -1,7 +1,7 @@ use atomicwrites; use common::make_io_err; use directory::Directory; -use directory::error::{OpenWriteError, OpenReadError, DeleteError, OpenDirectoryError}; +use directory::error::{IOError, OpenWriteError, OpenReadError, DeleteError, OpenDirectoryError}; use directory::ReadOnlySource; use directory::shared_vec_slice::SharedVecSlice; use directory::WritePtr; @@ -24,13 +24,15 @@ use std::sync::Weak; use tempdir::TempDir; fn open_mmap(full_path: &PathBuf) -> result::Result<Option<Arc<Mmap>>, OpenReadError> { - let convert_file_error = |err: io::Error| if err.kind() == io::ErrorKind::NotFound { - OpenReadError::FileDoesNotExist(full_path.clone()) - } else { - OpenReadError::IOError(err) - }; - let file = File::open(&full_path).map_err(convert_file_error)?; - let meta_data = file.metadata().map_err(OpenReadError::IOError)?; + let file = File::open(&full_path).map_err(|e| { + if e.kind() == io::ErrorKind::NotFound { + OpenReadError::FileDoesNotExist(full_path.clone()) + } else { + OpenReadError::IOError(IOError::with_path(full_path.to_owned(), e)) + } + })?; + + let meta_data = file.metadata().map_err(|e| IOError::with_path(full_path.to_owned(), e))?; if meta_data.len() == 0 { // if the file size is 0, it will not be possible // to mmap the file, so we return an anonymous mmap_cache @@ -39,7 +41,7 @@ fn open_mmap(full_path: &PathBuf) -> result::Result<Option<Arc<Mmap>>, OpenReadE } match Mmap::open(&file, Protection::Read) { Ok(mmap) => Ok(Some(Arc::new(mmap))), - Err(e) => Err(OpenReadError::IOError(e)), + Err(e) => Err(IOError::with_path(full_path.to_owned(), e))?, } } @@ -274,7 +276,7 @@ impl Directory for MmapDirectory { let msg = format!("Failed to acquired write lock \ on mmap cache while reading {:?}", path); - OpenReadError::IOError(make_io_err(msg)) + IOError::with_path(path.to_owned(), make_io_err(msg)) })?; Ok(mmap_cache @@ -295,17 +297,17 @@ impl Directory for MmapDirectory { let mut file = open_res .map_err(|err| if err.kind() == io::ErrorKind::AlreadyExists { - OpenWriteError::FileAlreadyExists(PathBuf::from(path)) + OpenWriteError::FileAlreadyExists(path.to_owned()) } else { - OpenWriteError::IOError(err) + IOError::with_path(path.to_owned(), err).into() })?; // making sure the file is created. - try!(file.flush()); + file.flush().map_err(|e| IOError::with_path(path.to_owned(), e))?; // Apparetntly, on some filesystem syncing the parent // directory is required. - try!(self.sync_directory()); + self.sync_directory().map_err(|e| IOError::with_path(path.to_owned(), e))?; let writer = SafeFileWriter::new(file); Ok(BufWriter::new(Box::new(writer))) @@ -320,19 +322,19 @@ impl Directory for MmapDirectory { let msg = format!("Failed to acquired write lock \ on mmap cache while deleting {:?}", path); - DeleteError::IOError(make_io_err(msg)) + IOError::with_path(path.to_owned(), make_io_err(msg)) })?; // Removing the entry in the MMap cache. // The munmap will appear on Drop, // when the last reference is gone. mmap_cache.cache.remove(&full_path); match fs::remove_file(&full_path) { - Ok(_) => self.sync_directory().map_err(DeleteError::IOError), + Ok(_) => self.sync_directory().map_err(|e| IOError::with_path(path.to_owned(), e).into()), Err(e) => { if e.kind() == io::ErrorKind::NotFound { Err(DeleteError::FileDoesNotExist(path.to_owned())) } else { - Err(DeleteError::IOError(e)) + Err(IOError::with_path(path.to_owned(), e).into()) } } } @@ -349,14 +351,14 @@ impl Directory for MmapDirectory { match File::open(&full_path) { Ok(mut file) => { file.read_to_end(&mut buffer) - .map_err(OpenReadError::IOError)?; + .map_err(|e| IOError::with_path(path.to_owned(), e))?; Ok(buffer) } Err(e) => { if e.kind() == io::ErrorKind::NotFound { Err(OpenReadError::FileDoesNotExist(path.to_owned())) } else { - Err(OpenReadError::IOError(e)) + Err(IOError::with_path(path.to_owned(), e).into()) } } } diff --git a/src/directory/ram_directory.rs b/src/directory/ram_directory.rs index 656eb73..1deb758 100644 --- a/src/directory/ram_directory.rs +++ b/src/directory/ram_directory.rs @@ -6,7 +6,7 @@ use std::result; use std::sync::{Arc, RwLock}; use common::make_io_err; use directory::{Directory, ReadOnlySource}; -use directory::error::{OpenWriteError, OpenReadError, DeleteError}; +use directory::error::{IOError, OpenWriteError, OpenReadError, DeleteError}; use directory::WritePtr; use super::shared_vec_slice::SharedVecSlice; @@ -97,7 +97,7 @@ impl InnerDirectory { directory when trying to read {:?}", path); let io_err = make_io_err(msg); - OpenReadError::IOError(io_err) + OpenReadError::IOError(IOError::with_path(path.to_owned(), io_err)) }) .and_then(|readable_map| { readable_map @@ -115,7 +115,7 @@ impl InnerDirectory { directory when trying to delete {:?}", path); let io_err = make_io_err(msg); - DeleteError::IOError(io_err) + DeleteError::IOError(IOError::with_path(path.to_owned(), io_err)) }) .and_then(|mut writable_map| match writable_map.remove(path) { Some(_) => Ok(()), @@ -163,8 +163,11 @@ impl Directory for RAMDirectory { fn open_write(&mut self, path: &Path) -> Result<WritePtr, OpenWriteError> { let path_buf = PathBuf::from(path); let vec_writer = VecWriter::new(path_buf.clone(), self.fs.clone()); + + let exists = self.fs.write(path_buf.clone(), &Vec::new()).map_err(|err| IOError::with_path(path.to_owned(), err))?; + // force the creation of the file to mimic the MMap directory. - if try!(self.fs.write(path_buf.clone(), &Vec::new())) { + if exists { Err(OpenWriteError::FileAlreadyExists(path_buf)) } else { Ok(BufWriter::new(Box::new(vec_writer))) diff --git a/src/error.rs b/src/error.rs index 83077c6..fa13d81 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,85 +5,108 @@ use std::io; use std::path::PathBuf; use std::error; use std::sync::PoisonError; -use directory::error::{OpenReadError, OpenWriteError, OpenDirectoryError}; +use directory::error::{IOError, OpenReadError, OpenWriteError, OpenDirectoryError}; use query; use schema; use fastfield::FastFieldNotAvailableError; use serde_json; - -/// Generic tantivy error. -/// -/// Any specialized error return in tantivy can be converted in `tantivy::Error`. -#[derive(Debug)] -pub enum Error { - /// Path does not exist. - PathDoesNotExist(PathBuf), - /// File already exists, this is a problem when we try to write into a new file. - FileAlreadyExists(PathBuf), - /// IO Error - IOError(io::Error), - /// A thread holding the locked panicked and poisoned the lock. - Poisoned, - /// The data within is corrupted. - /// - /// For instance, it contains invalid JSON. - CorruptedFile(PathBuf, Box<error::Error + Send + Sync>), - /// Invalid argument was passed by the user. - InvalidArgument(String), - /// An Error happened in one of the thread - ErrorInThread(String), - /// An Error appeared related to the lack of a field. - SchemaError(String), - /// Tried to access a fastfield reader for a field not configured accordingly. - FastFieldError(FastFieldNotAvailableError), -} +error_chain!( + errors { + PathDoesNotExist(buf: PathBuf) { + description("path does not exist") + display("path does not exist: '{:?}'", buf) + } + FileAlreadyExists(buf: PathBuf) { + description("file already exists") + display("file already exists: '{:?}'", buf) + } + IOError(err: IOError) { + description("an IO error occurred") + display("an IO error occurred: '{}'", err) + } + CorruptedFile(buf: PathBuf) { + description("file contains corrupted data") + display("file contains corrupted data: '{:?}'", buf) + } + Poisoned { + description("a thread holding the locked panicked and poisoned the lock") + } + InvalidArgument(arg: String) { + description("an invalid argument was passed") + display("an invalid argument was passed: '{}'", arg) + } + ErrorInThread(err: String) { + description("an error occurred in a thread") + display("an error occurred in a thread: '{}'", err) + } + SchemaError(field: String) { + description("a schema field is missing") + display("a schema field is missing: '{}'", field) + } + FastFieldError(err: FastFieldNotAvailableError) { + description("fast field not available") + display("fast field not available: '{:?}'", err) + } + } +); impl From<FastFieldNotAvailableError> for Error { fn from(fastfield_error: FastFieldNotAvailableError) -> Error { - Error::FastFieldError(fastfield_error) + ErrorKind::FastFieldError(fastfield_error).into() + } +} + +impl From<IOError> for Error { + fn from(io_error: IOError) -> Error { + ErrorKind::IOError(io_error).into() } } impl From<io::Error> for Error { fn from(io_error: io::Error) -> Error { - Error::IOError(io_error) + ErrorKind::IOError(io_error.into()).into() } } impl From<query::QueryParserError> for Error { fn from(parsing_error: query::QueryParserError) -> Error { - Error::InvalidArgument(format!("Query is invalid. {:?}", parsing_error)) + ErrorKind::InvalidArgument(format!("Query is invalid. {:?}", parsing_error)).into() } } impl<Guard> From<PoisonError<Guard>> for Error { fn from(_: PoisonError<Guard>) -> Error { - Error::Poisoned + ErrorKind::Poisoned.into() } } impl From<OpenReadError> for Error { fn from(error: OpenReadError) -> Error { match error { - OpenReadError::FileDoesNotExist(filepath) => Error::PathDoesNotExist(filepath), - OpenReadError::IOError(io_error) => Error::IOError(io_error), + OpenReadError::FileDoesNotExist(filepath) => { + ErrorKind::PathDoesNotExist(filepath).into() + } + OpenReadError::IOError(io_error) => ErrorKind::IOError(io_error).into(), } } } impl From<schema::DocParsingError> for Error { fn from(error: schema::DocParsingError) -> Error { - Error::InvalidArgument(format!("Failed to parse document {:?}", error)) + ErrorKind::InvalidArgument(format!("Failed to parse document {:?}", error)).into() } } impl From<OpenWriteError> for Error { fn from(error: OpenWriteError) -> Error { match error { - OpenWriteError::FileAlreadyExists(filepath) => Error::FileAlreadyExists(filepath), - OpenWriteError::IOError(io_error) => Error::IOError(io_error), - } + OpenWriteError::FileAlreadyExists(filepath) => { + ErrorKind::FileAlreadyExists(filepath) + } + OpenWriteError::IOError(io_error) => ErrorKind::IOError(io_error), + } + .into() } } @@ -91,17 +114,18 @@ impl From<OpenDirectoryError> for Error { fn from(error: OpenDirectoryError) -> Error { match error { OpenDirectoryError::DoesNotExist(directory_path) => { - Error::PathDoesNotExist(directory_path) + ErrorKind::PathDoesNotExist(directory_path).into() } OpenDirectoryError::NotADirectory(directory_path) => { - Error::InvalidArgument(format!("{:?} is not a directory", directory_path)) - } + ErrorKind::InvalidArgument(format!("{:?} is not a directory", directory_path)).into() + }, } } } impl From<serde_json::Error> for Error { fn from(error: serde_json::Error) -> Error { - Error::IOError(error.into()) + let io_err = io::Error::from(error); + ErrorKind::IOError(io_err.into()).into() } } diff --git a/src/indexer/index_writer.rs b/src/indexer/index_writer.rs index eb8117a..72b251d 100644 --- a/src/indexer/index_writer.rs +++ b/src/indexer/index_writer.rs @@ -9,7 +9,7 @@ use core::SegmentReader; use indexer::stamper::Stamper; use datastruct::stacker::Heap; use directory::FileProtection; -use Error; +use error::*; use Directory; use fastfield::write_delete_bitset; use indexer::delete_queue::{DeleteCursor, DeleteQueue}; @@ -22,7 +22,6 @@ use indexer::SegmentEntry; use indexer::SegmentWriter; use postings::DocSet; use postings::SegmentPostingsOption; -use Result; use schema::Document; use schema::Schema; use schema::Term; @@ -325,19 +324,17 @@ impl IndexWriter { let former_workers_handles = mem::replace(&mut self.workers_join_handle, vec![]); for join_handle in former_workers_handles { - try!(join_handle + join_handle .join() .expect("Indexing Worker thread panicked") - .map_err(|e| { - Error::ErrorInThread(format!("Error in indexing worker thread. {:?}", e)) - })); + .chain_err(|| ErrorKind::ErrorInThread("Error in indexing worker thread.".into()))?; } drop(self.workers_join_handle); let result = self.segment_updater .wait_merging_thread() - .map_err(|_| Error::ErrorInThread("Failed to join merging thread.".to_string())); + .chain_err(|| ErrorKind::ErrorInThread("Failed to join merging thread.".into())); if let Err(ref e) = result { error!("Some merging thread failed {:?}", e); @@ -527,12 +524,13 @@ impl IndexWriter { for worker_handle in former_workers_join_handle { let indexing_worker_result = - try!(worker_handle + worker_handle .join() - .map_err(|e| Error::ErrorInThread(format!("{:?}", e)))); - try!(indexing_worker_result); + .map_err(|e| Error::from(ErrorKind::ErrorInThread(format!("{:?}", e))))?; + + indexing_worker_result?; // add a new worker for the next generation. - try!(self.add_indexing_worker()); + self.add_indexing_worker()?; } diff --git a/src/indexer/merger.rs b/src/indexer/merger.rs index 4c19254..ab82deb 100644 --- a/src/indexer/merger.rs +++ b/src/indexer/merger.rs @@ -1,4 +1,4 @@ -use {Error, Result}; +use error::*; use core::SegmentReader; use core::Segment; use DocId; @@ -161,7 +161,7 @@ impl IndexMerger { let error_msg = format!("Failed to find a u64_reader for field {:?}", field); error!("{}", error_msg); - return Err(Error::SchemaError(error_msg)); + bail!(ErrorKind::SchemaError(error_msg)); } } } diff --git a/src/indexer/segment_updater.rs b/src/indexer/segment_updater.rs index 5446132..fad6a4c 100644 --- a/src/indexer/segment_updater.rs +++ b/src/indexer/segment_updater.rs @@ -7,7 +7,7 @@ use core::SegmentMeta; use core::SerializableSegment; use directory::Directory; use indexer::stamper::Stamper; -use Error; +use error::*; use futures_cpupool::CpuPool; use futures::Future; use futures::Canceled; @@ -19,7 +19,6 @@ use indexer::MergeCandidate; use indexer::merger::IndexMerger; use indexer::SegmentEntry; use indexer::SegmentSerializer; -use Result; use futures_cpupool::CpuFuture; use serde_json; use indexer::delete_queue::DeleteCursor; @@ -117,7 +116,7 @@ fn perform_merge(segment_ids: &[SegmentId], error!("Error, had to abort merge as some of the segment is not managed anymore."); let msg = format!("Segment {:?} requested for merge is not managed.", segment_id); - return Err(Error::InvalidArgument(msg)); + bail!(ErrorKind::InvalidArgument(msg)); } } @@ -447,8 +446,7 @@ impl SegmentUpdater { for (_, merging_thread_handle) in new_merging_threads { merging_thread_handle .join() - .map(|_| ()) - .map_err(|_| Error::ErrorInThread("Merging thread failed.".to_string()))? + .map_err(|_| ErrorKind::ErrorInThread("Merging thread failed.".into()))?; } // Our merging thread may have queued their completed self.run_async(move |_| {}).wait()?; @@ -36,6 +36,9 @@ extern crate serde_derive; extern crate log; #[macro_use] +extern crate error_chain; + +#[macro_use] extern crate version; extern crate fst; extern crate byteorder; |