Use thiserror

This commit is contained in:
Marcel Müller
2020-07-30 15:16:10 +02:00
parent 5695aab1b3
commit b752e1e209
10 changed files with 192 additions and 280 deletions
+6 -1
View File
@@ -16,9 +16,9 @@ version = "2.0.0-rc3"
all-features = true
[dependencies]
failure = "0.1.1"
serde = "1"
tempfile = "3"
thiserror = "1.0.20"
[dependencies.ron]
optional = true
@@ -40,6 +40,10 @@ version = "0.8.5"
optional = true
version = "0.7"
[dependencies.anyhow]
optional = true
version = "1.0.32"
[dev-dependencies]
lazy_static = "1"
serde_derive = "1"
@@ -49,5 +53,6 @@ default = []
ron_enc = ["ron"]
bin_enc = ["bincode", "base64"]
yaml_enc = ["serde_yaml"]
other_errors = ["anyhow"]
mmap = ["memmap"]
+1 -2
View File
@@ -41,12 +41,11 @@ features = ["ron_enc"] # You can also use "yaml_enc" or "bin_enc"
```
```rust
extern crate failure;
extern crate rustbreak;
use std::collections::HashMap;
use rustbreak::{MemoryDatabase, deser::Ron};
fn main() -> Result<(), failure::Error> {
fn main() -> rustbreak::Result<()> {
let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
println!("Writing to Database");
+2 -3
View File
@@ -1,6 +1,5 @@
#[macro_use]
extern crate serde_derive;
use failure;
use rustbreak::deser::Ron;
use rustbreak::FileDatabase;
@@ -17,7 +16,7 @@ struct Person {
country: Country,
}
fn do_main() -> Result<(), failure::Error> {
fn do_main() -> Result<(), rustbreak::RustbreakError> {
use std::collections::HashMap;
let db = FileDatabase::<HashMap<String, Person>, Ron>::load_from_path_or_default("test.ron")?;
@@ -58,7 +57,7 @@ fn do_main() -> Result<(), failure::Error> {
fn main() {
if let Err(e) = do_main() {
eprintln!("An error has occurred at: \n{}", e.backtrace());
eprintln!("An error has occurred at: \n{}", e);
std::process::exit(1);
}
}
+2 -3
View File
@@ -1,6 +1,5 @@
#[macro_use]
extern crate serde_derive;
use failure;
use rustbreak::deser::{Ron, Yaml};
use rustbreak::{backend::FileBackend, FileDatabase};
@@ -17,7 +16,7 @@ struct Person {
country: Country,
}
fn do_main() -> Result<(), failure::Error> {
fn do_main() -> Result<(), rustbreak::RustbreakError> {
use std::collections::HashMap;
let db = FileDatabase::<HashMap<String, Person>, Ron>::load_from_path_or_default("test.ron")?;
@@ -56,7 +55,7 @@ fn do_main() -> Result<(), failure::Error> {
fn main() {
if let Err(e) = do_main() {
eprintln!("An error has occurred at: \n{}", e.backtrace());
eprintln!("An error has occurred at: \n{}", e);
std::process::exit(1);
}
}
+13 -21
View File
@@ -1,5 +1,3 @@
use failure::ResultExt;
use super::Backend;
use crate::error;
@@ -32,11 +30,11 @@ impl Mmap {
}
/// Copies data to mmap and modifies data's end cursor.
fn write(&mut self, data: &[u8]) -> Result<(), failure::Error> {
fn write(&mut self, data: &[u8]) -> error::BackendResult<()> {
if data.len() > self.len {
return Err(failure::err_msg(
"Unexpected write beyond mmap's backend capacity. This is a rustbreak's bug",
));
return Err(error::BackendError::Internal(format!(
"Unexpected write beyond mmap's backend capacity."
)));
}
self.end = data.len();
self.as_mut_slice().copy_from_slice(data);
@@ -59,7 +57,7 @@ impl Mmap {
}
}
/// A backend that uses anonymous mmap.
/// A backend that uses an nonymous mmap.
///
/// The `Backend` automatically creates bigger map
/// on demand using following strategy:
@@ -77,38 +75,32 @@ pub struct MmapStorage {
impl MmapStorage {
/// Creates new storage with 1024 bytes.
pub fn new() -> error::Result<Self> {
pub fn new() -> error::BackendResult<Self> {
Self::with_size(1024)
}
/// Creates new storage with custom size.
pub fn with_size(len: usize) -> error::Result<Self> {
let mmap = Mmap::new(len).context(error::RustbreakErrorKind::Backend)?;
pub fn with_size(len: usize) -> error::BackendResult<Self> {
let mmap = Mmap::new(len)?;
Ok(Self { mmap })
}
}
impl Backend for MmapStorage {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
let mmap = self.mmap.as_slice();
let mut buffer = Vec::with_capacity(mmap.len());
buffer.extend_from_slice(mmap);
Ok(buffer)
}
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
if self.mmap.len < data.len() {
self.mmap
.resize_no_copy(data.len())
.context(error::RustbreakErrorKind::Backend)?;
self.mmap.resize_no_copy(data.len())?;
}
self.mmap
.write(data)
.context(error::RustbreakErrorKind::Backend)?;
self.mmap
.flush()
.context(error::RustbreakErrorKind::Backend)?;
self.mmap.write(data)?;
self.mmap.flush()?;
Ok(())
}
}
+29 -45
View File
@@ -10,9 +10,7 @@
//! Implementing your own Backend should be straightforward. Check the `Backend`
//! documentation for details.
use failure::ResultExt;
use crate::error::{self, RustbreakErrorKind as ErrorKind};
use crate::error;
/// The Backend Trait.
///
@@ -21,31 +19,31 @@ use crate::error::{self, RustbreakErrorKind as ErrorKind};
/// same dataset.
pub trait Backend {
/// Read the all data from the backend.
fn get_data(&mut self) -> error::Result<Vec<u8>>;
fn get_data(&mut self) -> error::BackendResult<Vec<u8>>;
/// Write the whole slice to the backend.
fn put_data(&mut self, data: &[u8]) -> error::Result<()>;
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()>;
}
impl Backend for Box<dyn Backend> {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
use std::ops::DerefMut;
self.deref_mut().get_data()
}
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
use std::ops::DerefMut;
self.deref_mut().put_data(data)
}
}
impl<T: Backend> Backend for Box<T> {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
use std::ops::DerefMut;
self.deref_mut().get_data()
}
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
use std::ops::DerefMut;
self.deref_mut().put_data(data)
}
@@ -64,28 +62,22 @@ pub use path::PathBackend;
pub struct FileBackend(std::fs::File);
impl Backend for FileBackend {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
use std::io::{Read, Seek, SeekFrom};
let mut buffer = vec![];
self.0
.seek(SeekFrom::Start(0))
.context(ErrorKind::Backend)?;
self.0
.read_to_end(&mut buffer)
.context(ErrorKind::Backend)?;
self.0.seek(SeekFrom::Start(0))?;
self.0.read_to_end(&mut buffer)?;
Ok(buffer)
}
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
use std::io::{Seek, SeekFrom, Write};
self.0
.seek(SeekFrom::Start(0))
.context(ErrorKind::Backend)?;
self.0.set_len(0).context(ErrorKind::Backend)?;
self.0.write_all(data).context(ErrorKind::Backend)?;
self.0.sync_all().context(ErrorKind::Backend)?;
self.0.seek(SeekFrom::Start(0))?;
self.0.set_len(0)?;
self.0.write_all(data)?;
self.0.sync_all()?;
Ok(())
}
}
@@ -107,23 +99,19 @@ impl FileBackend {
impl FileBackend {
/// Opens a new [`FileBackend`] for a given path.
/// Errors when the file doesn't yet exist.
pub fn from_path_or_fail<P: AsRef<std::path::Path>>(path: P) -> error::Result<Self> {
pub fn from_path_or_fail<P: AsRef<std::path::Path>>(path: P) -> error::BackendResult<Self> {
use std::fs::OpenOptions;
Ok(Self(
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.context(ErrorKind::Backend)?,
))
Ok(Self(OpenOptions::new().read(true).write(true).open(path)?))
}
/// Opens a new [`FileBackend`] for a given path.
/// Creates a file if it doesn't yet exist.
///
/// Returns the [`FileBackend`] and whether the file already existed.
pub fn from_path_or_create<P: AsRef<std::path::Path>>(path: P) -> error::Result<(Self, bool)> {
pub fn from_path_or_create<P: AsRef<std::path::Path>>(
path: P,
) -> error::BackendResult<(Self, bool)> {
use std::fs::OpenOptions;
let exists = path.as_ref().is_file();
@@ -133,8 +121,7 @@ impl FileBackend {
.read(true)
.write(true)
.create(true)
.open(path)
.context(ErrorKind::Backend)?,
.open(path)?,
),
exists,
))
@@ -142,7 +129,7 @@ impl FileBackend {
/// Opens a new [`FileBackend`] for a given path.
/// Creates a file if it doesn't yet exist, and calls `closure` with it.
pub fn from_path_or_create_and<P, C>(path: P, closure: C) -> error::Result<Self>
pub fn from_path_or_create_and<P, C>(path: P, closure: C) -> error::BackendResult<Self>
where
C: FnOnce(&mut std::fs::File),
P: AsRef<std::path::Path>,
@@ -171,12 +158,12 @@ impl MemoryBackend {
}
impl Backend for MemoryBackend {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
println!("Returning data: {:?}", &self.0);
Ok(self.0.clone())
}
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
println!("Writing data: {:?}", data);
self.0 = data.to_owned();
Ok(())
@@ -186,7 +173,6 @@ impl Backend for MemoryBackend {
#[cfg(test)]
mod tests {
use super::{Backend, FileBackend, MemoryBackend};
use failure::Fail;
use std::io::{Read, Seek, SeekFrom, Write};
use tempfile::NamedTempFile;
@@ -263,13 +249,11 @@ mod tests {
file_path.push("rustbreak_path_db.db");
let err =
FileBackend::from_path_or_fail(file_path).expect_err("should fail with file not found");
assert_eq!(crate::error::RustbreakErrorKind::Backend, err.kind());
let io_err = err
.cause()
.expect("error has no cause")
.downcast_ref::<std::io::Error>()
.expect("error is not an io error");
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
if let crate::error::BackendError::Io(io_err) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong kind of error returned: {}", err);
};
dir.close().expect("Error while deleting temp directory!");
}
+19 -35
View File
@@ -7,8 +7,6 @@
use super::Backend;
use crate::error;
use crate::error::RustbreakErrorKind as ErrorKind;
use failure::ResultExt;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};
use tempfile::NamedTempFile;
@@ -25,11 +23,8 @@ pub struct PathBackend {
impl PathBackend {
/// Opens a new [`PathBackend`] for a given path.
/// Errors when the file doesn't yet exist.
pub fn from_path_or_fail(path: PathBuf) -> error::Result<Self> {
OpenOptions::new()
.read(true)
.open(path.as_path())
.context(ErrorKind::Backend)?;
pub fn from_path_or_fail(path: PathBuf) -> error::BackendResult<Self> {
OpenOptions::new().read(true).open(path.as_path())?;
Ok(Self { path })
}
@@ -37,19 +32,18 @@ impl PathBackend {
/// Creates a file if it doesn't yet exist.
///
/// Returns the [`PathBackend`] and whether the file already existed.
pub fn from_path_or_create(path: PathBuf) -> error::Result<(Self, bool)> {
pub fn from_path_or_create(path: PathBuf) -> error::BackendResult<(Self, bool)> {
let exists = path.as_path().is_file();
OpenOptions::new()
.write(true)
.create(true)
.open(path.as_path())
.context(ErrorKind::Backend)?;
.open(path.as_path())?;
Ok((Self { path }, exists))
}
/// Opens a new [`PathBackend`] for a given path.
/// Creates a file if it doesn't yet exist, and calls `closure` with it.
pub fn from_path_or_create_and<C>(path: PathBuf, closure: C) -> error::Result<Self>
pub fn from_path_or_create_and<C>(path: PathBuf, closure: C) -> error::BackendResult<Self>
where
C: FnOnce(&mut std::fs::File),
{
@@ -58,8 +52,7 @@ impl PathBackend {
.read(true)
.write(true)
.create(true)
.open(path.as_path())
.context(ErrorKind::Backend)?;
.open(path.as_path())?;
if !exists {
closure(&mut file)
}
@@ -68,15 +61,12 @@ impl PathBackend {
}
impl Backend for PathBackend {
fn get_data(&mut self) -> error::Result<Vec<u8>> {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
use std::io::Read;
let mut file = OpenOptions::new()
.read(true)
.open(self.path.as_path())
.context(ErrorKind::Backend)?;
let mut file = OpenOptions::new().read(true).open(self.path.as_path())?;
let mut buffer = vec![];
file.read_to_end(&mut buffer).context(ErrorKind::Backend)?;
file.read_to_end(&mut buffer)?;
Ok(buffer)
}
@@ -84,17 +74,14 @@ impl Backend for PathBackend {
///
/// This won't corrupt the existing database file if the program panics
/// during the save.
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
use std::io::Write;
#[allow(clippy::or_fun_call)] // `Path::new` is a zero cost conversion
let mut tempf = NamedTempFile::new_in(self.path.parent().unwrap_or(Path::new(".")))
.context(ErrorKind::Backend)?;
tempf.write_all(data).context(ErrorKind::Backend)?;
tempf.as_file().sync_all().context(ErrorKind::Backend)?;
tempf
.persist(self.path.as_path())
.context(ErrorKind::Backend)?;
let mut tempf = NamedTempFile::new_in(self.path.parent().unwrap_or(Path::new(".")))?;
tempf.write_all(data)?;
tempf.as_file().sync_all()?;
tempf.persist(self.path.as_path())?;
Ok(())
}
}
@@ -102,7 +89,6 @@ impl Backend for PathBackend {
#[cfg(test)]
mod tests {
use super::{Backend, PathBackend};
use failure::Fail;
use std::io::Write;
use tempfile::NamedTempFile;
@@ -155,13 +141,11 @@ mod tests {
file_path.push("rustbreak_path_db.db");
let err =
PathBackend::from_path_or_fail(file_path).expect_err("should fail with file not found");
assert_eq!(crate::error::RustbreakErrorKind::Backend, err.kind());
let io_err = err
.cause()
.expect("error has no cause")
.downcast_ref::<std::io::Error>()
.expect("error is not an io error");
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
if let crate::error::BackendError::Io(io_err) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong kind of error returned: {}", err);
};
dir.close().expect("Error while deleting temp directory!");
}
+18 -15
View File
@@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::error;
use std::io::Read;
use serde::de::DeserializeOwned;
@@ -26,11 +27,10 @@ pub use self::bincode::Bincode;
///
/// ```rust
/// extern crate rustbreak;
/// extern crate thiserror;
/// extern crate serde;
/// #[macro_use]
/// extern crate failure;
///
/// use failure::{Backtrace, Context, Fail};
/// use serde::de::Deserialize;
/// use serde::Serialize;
/// use std::io::Read;
@@ -38,8 +38,8 @@ pub use self::bincode::Bincode;
/// use rustbreak::deser::DeSerializer;
/// use rustbreak::error;
///
/// #[derive(Fail, Debug)]
/// #[fail(display = "A FrobnarError ocurred")]
/// #[derive(Clone, Debug, thiserror::Error)]
/// #[error("A frobnarizer could not splagrle.")]
/// struct FrobnarError;
///
/// fn to_frobnar<T: Serialize>(input: &T) -> Vec<u8> {
@@ -57,12 +57,12 @@ pub use self::bincode::Bincode;
/// where
/// for<'de> T: Deserialize<'de>,
/// {
/// fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
/// fn serialize(&self, val: &T) -> rustbreak::DeSerResult<Vec<u8>> {
/// Ok(to_frobnar(val))
/// }
///
/// fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
/// Ok(from_frobnar(&s)?)
/// fn deserialize<R: Read>(&self, s: R) -> rustbreak::DeSerResult<T> {
/// Ok(from_frobnar(&s).map_err(|e| error::DeSerError::Other(e.into()))?)
/// }
/// }
///
@@ -72,9 +72,9 @@ pub trait DeSerializer<T: Serialize + DeserializeOwned>:
std::default::Default + Send + Sync + Clone
{
/// Serializes a given value to a [`String`].
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error>;
fn serialize(&self, val: &T) -> error::DeSerResult<Vec<u8>>;
/// Deserializes a [`String`] to a value.
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error>;
fn deserialize<R: Read>(&self, s: R) -> error::DeSerResult<T>;
}
#[cfg(feature = "ron_enc")]
@@ -89,16 +89,17 @@ mod ron {
use ron::ser::PrettyConfig;
use crate::deser::DeSerializer;
use crate::error;
/// The Struct that allows you to use `ron` the Rusty Object Notation.
#[derive(Debug, Default, Clone)]
pub struct Ron;
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Ron {
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
fn serialize(&self, val: &T) -> error::DeSerResult<Vec<u8>> {
Ok(to_ron_string(val, PrettyConfig::default()).map(String::into_bytes)?)
}
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
fn deserialize<R: Read>(&self, s: R) -> error::DeSerResult<T> {
Ok(from_ron_string(s)?)
}
}
@@ -113,16 +114,17 @@ mod yaml {
use serde_yaml::{from_reader as from_yaml_string, to_string as to_yaml_string};
use crate::deser::DeSerializer;
use crate::error;
/// The struct that allows you to use yaml.
#[derive(Debug, Default, Clone)]
pub struct Yaml;
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Yaml {
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
fn serialize(&self, val: &T) -> error::DeSerResult<Vec<u8>> {
Ok(to_yaml_string(val).map(String::into_bytes)?)
}
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
fn deserialize<R: Read>(&self, s: R) -> error::DeSerResult<T> {
Ok(from_yaml_string(s)?)
}
}
@@ -137,16 +139,17 @@ mod bincode {
use serde::Serialize;
use crate::deser::DeSerializer;
use crate::error;
/// The struct that allows you to use bincode
#[derive(Debug, Default, Clone)]
pub struct Bincode;
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Bincode {
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
fn serialize(&self, val: &T) -> error::DeSerResult<Vec<u8>> {
Ok(serialize(val)?)
}
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
fn deserialize<R: Read>(&self, s: R) -> error::DeSerResult<T> {
Ok(deserialize_from(s)?)
}
}
+49 -80
View File
@@ -2,97 +2,66 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use failure::{Backtrace, Context, Fail};
use std::fmt::{self, Display};
/// An error returned by a DeSer implementor
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum DeSerError {
#[cfg(feature = "yaml_enc")]
/// An error occured with Yaml
#[error("An error with yaml occured")]
Yaml(#[from] serde_yaml::Error),
#[cfg(feature = "ron_enc")]
/// An error occured with Ron
#[error("An error with Ron occured")]
Ron(#[from] ron::Error),
#[cfg(feature = "bin_enc")]
/// An error occured with Bincode
#[error("An error with Bincode occured")]
Bincode(#[from] std::boxed::Box<bincode::ErrorKind>),
#[cfg(feature = "other_errors")]
/// An error occured with Bincode
#[error(transparent)]
Other(#[from] anyhow::Error),
}
/// An error returned by a Backend implementor
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum BackendError {
/// An error occured from the tempfile
#[error("An error while persisting the file occured")]
TempFile(#[from] tempfile::PersistError),
/// An I/O Error occured
#[error("An I/O Error occured")]
Io(#[from] std::io::Error),
/// An internal error to Rustbreak occured
#[error("An internal error to rustbreak occured, please report it to the maintainers")]
Internal(String),
}
/// The different kinds of errors that can be returned
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum RustbreakErrorKind {
/// A context error when a serialization failed
#[fail(display = "Could not serialize the value")]
Serialization,
/// A context error when a deserialization failed
#[fail(display = "Could not deserialize the value")]
Deserialization,
pub enum RustbreakError {
/// A context error when a DeSerialization failed
#[error("Could not deserialize the value")]
DeSerialization(#[from] DeSerError),
/// This error is returned if the `Database` is poisoned. See
/// `Database::write` for details
#[fail(display = "The database has been poisoned")]
#[error("The database has been poisoned")]
Poison,
/// An error in the backend happened
#[fail(display = "The backend has encountered an error")]
Backend,
#[error("The backend has encountered an error")]
Backend(#[from] BackendError),
/// If `Database::write_safe` is used and the closure panics, this error is
/// returned
#[fail(display = "The write operation paniced but got caught")]
#[error("The write operation paniced but got caught")]
WritePanic,
}
/// The main error type that gets returned for errors that happen while
/// interacting with a `Database`.
#[derive(Debug)]
pub struct RustbreakError {
inner: Context<RustbreakErrorKind>,
}
impl Fail for RustbreakError {
fn cause(&self) -> Option<&dyn Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Display for RustbreakError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl RustbreakError {
/// Get the kind of this error
pub fn kind(&self) -> RustbreakErrorKind {
*self.inner.get_context()
}
}
impl From<RustbreakErrorKind> for RustbreakError {
fn from(kind: RustbreakErrorKind) -> Self {
Self {
inner: Context::new(kind),
}
}
}
impl From<Context<RustbreakErrorKind>> for RustbreakError {
fn from(inner: Context<RustbreakErrorKind>) -> Self {
Self { inner }
}
}
/// A simple type alias for errors
pub type Result<T> = std::result::Result<T, RustbreakError>;
#[cfg(test)]
mod tests {
use super::{RustbreakError, RustbreakErrorKind};
use failure::Context;
use std::any::Any;
#[test]
fn static_errorkind_impl_any() {
let err = RustbreakErrorKind::Backend;
let boxed: Box<dyn Any> = Box::new(err);
assert!(boxed.is::<RustbreakErrorKind>());
}
#[test]
fn static_error_impl_any() {
let context = RustbreakErrorKind::Serialization;
let err: RustbreakError = Context::new(context).into();
let boxed: Box<dyn Any> = Box::new(err);
assert!(boxed.is::<RustbreakError>());
}
}
/// The type alias used for backends
pub type BackendResult<T> = std::result::Result<T, BackendError>;
/// The type alias used for DeSers
pub type DeSerResult<T> = std::result::Result<T, DeSerError>;
+53 -75
View File
@@ -81,13 +81,12 @@
//! ```
//!
//! ```rust
//! # extern crate failure;
//! # extern crate rustbreak;
//! # use std::collections::HashMap;
//! use rustbreak::{deser::Ron, MemoryDatabase};
//!
//! # fn main() {
//! # let func = || -> Result<(), failure::Error> {
//! # let func = || -> Result<(), Box<dyn std::error::Error>> {
//! let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
//!
//! println!("Writing to Database");
@@ -108,13 +107,12 @@
//!
//! Or alternatively:
//! ```rust
//! # extern crate failure;
//! # extern crate rustbreak;
//! # use std::collections::HashMap;
//! use rustbreak::{deser::Ron, MemoryDatabase};
//!
//! # fn main() {
//! # let func = || -> Result<(), failure::Error> {
//! # let func = || -> Result<(), Box<dyn std::error::Error>> {
//! let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
//!
//! println!("Writing to Database");
@@ -137,9 +135,6 @@
//! fail case as [`error::RustbreakError`]. This means that you can now either
//! continue bubbling up said error case, or handle it yourself.
//!
//! You can simply call its `.kind()` method, giving you all the information you
//! need to continue.
//!
//! ```rust
//! use rustbreak::{deser::Ron, error::RustbreakError, MemoryDatabase};
//! let db = match MemoryDatabase::<usize, Ron>::memory(0) {
@@ -194,7 +189,6 @@
//! [daybreak]: https://propublica.github.io/daybreak
//! [examples]: https://github.com/TheNeikos/rustbreak/tree/master/examples
//! [ron]: https://github.com/ron-rs/ron
//! [failure]: https://boats.gitlab.io/failure/intro.html
//! [features]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features
pub mod backend;
@@ -206,14 +200,11 @@ pub mod error;
/// The `DeSerializer` trait used by serialization structs
pub use crate::deser::DeSerializer;
/// The general error used by the Rustbreak Module
pub use crate::error::RustbreakError;
use std::fmt::Debug;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use failure::ResultExt;
use serde::de::DeserializeOwned;
use serde::Serialize;
@@ -221,7 +212,7 @@ use serde::Serialize;
use crate::backend::MmapStorage;
use crate::backend::{Backend, FileBackend, MemoryBackend, PathBackend};
use crate::error::RustbreakErrorKind as ErrorKind;
pub use crate::error::*;
/// The Central Database to Rustbreak.
///
@@ -236,7 +227,7 @@ use crate::error::RustbreakErrorKind as ErrorKind;
///
/// If the backend or the de/serialization panics, the database is poisoned.
/// This means that any subsequent writes/reads will fail with an
/// [`error::RustbreakErrorKind::Poison`]. You can only recover from this by
/// [`error::RustbreakError::Poison`]. You can only recover from this by
/// re-creating the Database Object.
#[derive(Debug)]
pub struct Database<Data, Back, DeSer> {
@@ -261,7 +252,7 @@ where
///
/// If you panic in the closure, the database is poisoned. This means that
/// any subsequent writes/reads will fail with an
/// [`error::RustbreakErrorKind::Poison`]. You can only recover from
/// [`error::RustbreakError::Poison`]. You can only recover from
/// this by re-creating the Database Object.
///
/// If you do not have full control over the code being written, and cannot
@@ -275,7 +266,6 @@ where
/// # extern crate rustbreak;
/// # extern crate serde;
/// # extern crate tempfile;
/// # extern crate failure;
/// use rustbreak::{deser::Ron, FileDatabase};
///
/// #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -284,7 +274,7 @@ where
/// }
///
/// # fn main() {
/// # let func = || -> Result<(), failure::Error> {
/// # let func = || -> Result<(), Box<dyn std::error::Error>> {
/// # let file = tempfile::tempfile()?;
/// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
///
@@ -305,7 +295,7 @@ where
where
T: FnOnce(&mut Data) -> R,
{
let mut lock = self.data.write().map_err(|_| ErrorKind::Poison)?;
let mut lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
Ok(task(&mut lock))
}
@@ -329,7 +319,7 @@ where
/// # Panics
///
/// When the closure panics, it is caught and a
/// [`error::RustbreakErrorKind::WritePanic`] will be returned.
/// [`error::RustbreakError::WritePanic`] will be returned.
///
/// # Examples
///
@@ -338,10 +328,9 @@ where
/// # extern crate rustbreak;
/// # extern crate serde;
/// # extern crate tempfile;
/// # extern crate failure;
/// use rustbreak::{
/// deser::Ron,
/// error::{RustbreakError, RustbreakErrorKind},
/// error::RustbreakError,
/// FileDatabase,
/// };
///
@@ -351,7 +340,7 @@ where
/// }
///
/// # fn main() {
/// # let func = || -> Result<(), failure::Error> {
/// # let func = || -> Result<(), Box<dyn std::error::Error>> {
/// # let file = tempfile::tempfile()?;
/// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
///
@@ -362,8 +351,8 @@ where
/// })
/// .expect_err("This should have been caught");
///
/// match result.kind() {
/// RustbreakErrorKind::WritePanic => {
/// match result {
/// RustbreakError::WritePanic => {
/// // We can now handle this, in this example we will just ignore it
/// }
/// e => {
@@ -388,12 +377,12 @@ where
where
T: FnOnce(&mut Data) + std::panic::UnwindSafe,
{
let mut lock = self.data.write().map_err(|_| ErrorKind::Poison)?;
let mut lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
let mut data = lock.clone();
std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
task(&mut data);
}))
.map_err(|_| ErrorKind::WritePanic)?;
.map_err(|_| RustbreakError::WritePanic)?;
*lock = data;
Ok(())
}
@@ -407,19 +396,19 @@ where
///
/// May return:
///
/// - [`error::RustbreakErrorKind::Backend`]
/// - [`error::RustbreakError::Backend`]
///
/// # Panics
///
/// If you panic in the closure, the database is poisoned. This means that
/// any subsequent writes/reads will fail with an
/// [`error::RustbreakErrorKind::Poison`]. You can only recover from
/// [`error::RustbreakError::Poison`]. You can only recover from
/// this by re-creating the Database Object.
pub fn read<T, R>(&self, task: T) -> error::Result<R>
where
T: FnOnce(&Data) -> R,
{
let mut lock = self.data.read().map_err(|_| ErrorKind::Poison)?;
let mut lock = self.data.read().map_err(|_| RustbreakError::Poison)?;
Ok(task(&mut lock))
}
@@ -435,7 +424,6 @@ where
/// # extern crate rustbreak;
/// # extern crate serde;
/// # extern crate tempfile;
/// # extern crate failure;
/// use rustbreak::{deser::Ron, FileDatabase};
///
/// #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -444,7 +432,7 @@ where
/// }
///
/// # fn main() {
/// # let func = || -> Result<(), failure::Error> {
/// # let func = || -> Result<(), Box<dyn std::error::Error>> {
/// # let file = tempfile::tempfile()?;
/// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
///
@@ -461,7 +449,7 @@ where
/// # }
/// ```
pub fn borrow_data<'a>(&'a self) -> error::Result<RwLockReadGuard<'a, Data>> {
self.data.read().map_err(|_| ErrorKind::Poison.into())
self.data.read().map_err(|_| RustbreakError::Poison.into())
}
/// Write lock the database and get access to the underlying struct.
@@ -473,7 +461,7 @@ where
///
/// If you panic while holding this reference, the database is poisoned.
/// This means that any subsequent writes/reads will fail with an
/// [`error::RustbreakErrorKind::Poison`]. You can only recover from
/// [`error::RustbreakError::Poison`]. You can only recover from
/// this by re-creating the Database Object.
///
/// If you do not have full control over the code being written, and cannot
@@ -487,7 +475,6 @@ where
/// # extern crate rustbreak;
/// # extern crate serde;
/// # extern crate tempfile;
/// # extern crate failure;
/// use rustbreak::{deser::Ron, FileDatabase};
///
/// #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -496,7 +483,7 @@ where
/// }
///
/// # fn main() {
/// # let func = || -> Result<(), failure::Error> {
/// # let func = || -> Result<(), Box<dyn std::error::Error>> {
/// # let file = tempfile::tempfile()?;
/// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
///
@@ -514,26 +501,24 @@ where
/// # }
/// ```
pub fn borrow_data_mut<'a>(&'a self) -> error::Result<RwLockWriteGuard<'a, Data>> {
self.data.write().map_err(|_| ErrorKind::Poison.into())
self.data.write().map_err(|_| RustbreakError::Poison.into())
}
/// Load data from backend and return this data.
fn load_from_backend(backend: &mut Back, deser: &DeSer) -> error::Result<Data> {
let new_data = deser
.deserialize(&backend.get_data()?[..])
.context(ErrorKind::Deserialization)?;
let new_data = deser.deserialize(&backend.get_data()?[..])?;
Ok(new_data)
}
/// Like [`Self::load`] but returns the write lock to data it used.
fn load_get_data_lock(&self) -> error::Result<RwLockWriteGuard<'_, Data>> {
let mut backend_lock = self.backend.lock().map_err(|_| ErrorKind::Poison)?;
let mut backend_lock = self.backend.lock().map_err(|_| RustbreakError::Poison)?;
let fresh_data = Self::load_from_backend(&mut backend_lock, &self.deser)?;
drop(backend_lock);
let mut data_write_lock = self.data.write().map_err(|_| ErrorKind::Poison)?;
let mut data_write_lock = self.data.write().map_err(|_| RustbreakError::Poison)?;
*data_write_lock = fresh_data;
Ok(data_write_lock)
}
@@ -545,20 +530,17 @@ where
/// Like [`Self::save`] but with explicit read (or write) lock to data.
fn save_data_locked<L: Deref<Target = Data>>(&self, lock: L) -> error::Result<()> {
let ser = self
.deser
.serialize(lock.deref())
.context(ErrorKind::Serialization)?;
let ser = self.deser.serialize(lock.deref())?;
drop(lock);
let mut backend = self.backend.lock().map_err(|_| ErrorKind::Poison)?;
let mut backend = self.backend.lock().map_err(|_| RustbreakError::Poison)?;
backend.put_data(&ser)?;
Ok(())
}
/// Flush the data structure to the backend.
pub fn save(&self) -> error::Result<()> {
let data = self.data.read().map_err(|_| ErrorKind::Poison)?;
let data = self.data.read().map_err(|_| RustbreakError::Poison)?;
self.save_data_locked(data)
}
@@ -570,7 +552,7 @@ where
let data = if load {
self.load_get_data_lock()?
} else {
self.data.write().map_err(|_| ErrorKind::Poison)?
self.data.write().map_err(|_| RustbreakError::Poison)?
};
Ok(data.clone())
}
@@ -579,7 +561,7 @@ where
///
/// To save the data afterwards, call with `save` true.
pub fn put_data(&self, new_data: Data, save: bool) -> error::Result<()> {
let mut data = self.data.write().map_err(|_| ErrorKind::Poison)?;
let mut data = self.data.write().map_err(|_| RustbreakError::Poison)?;
*data = new_data;
if save {
self.save_data_locked(data)
@@ -600,8 +582,10 @@ where
/// Break a database into its individual parts.
pub fn into_inner(self) -> error::Result<(Data, Back, DeSer)> {
Ok((
self.data.into_inner().map_err(|_| ErrorKind::Poison)?,
self.backend.into_inner().map_err(|_| ErrorKind::Poison)?,
self.data.into_inner().map_err(|_| RustbreakError::Poison)?,
self.backend
.into_inner()
.map_err(|_| RustbreakError::Poison)?,
self.deser,
))
}
@@ -619,7 +603,6 @@ where
/// # extern crate rustbreak;
/// # extern crate serde;
/// # extern crate tempfile;
/// # extern crate failure;
/// use rustbreak::{deser::Ron, FileDatabase};
///
/// #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -628,7 +611,7 @@ where
/// }
///
/// # fn main() {
/// # let func = || -> Result<(), failure::Error> {
/// # let func = || -> Result<(), Box<dyn std::error::Error>> {
/// # let file = tempfile::tempfile()?;
/// let db = FileDatabase::<Data, Ron>::from_file(file, Data { level: 0 })?;
///
@@ -650,7 +633,7 @@ where
/// # }
/// ```
pub fn try_clone(&self) -> error::Result<MemoryDatabase<Data, DeSer>> {
let lock = self.data.read().map_err(|_| ErrorKind::Poison)?;
let lock = self.data.read().map_err(|_| RustbreakError::Poison)?;
Ok(Database {
data: RwLock::new(lock.clone()),
@@ -698,7 +681,7 @@ where
let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
@@ -732,7 +715,7 @@ where
} else {
let data = closure();
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
data
@@ -759,7 +742,7 @@ where
let (mut backend, exists) = FileBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
@@ -833,7 +816,7 @@ where
let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
@@ -866,7 +849,7 @@ where
} else {
let data = closure();
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
data
@@ -890,7 +873,7 @@ where
let (mut backend, exists) = PathBackend::from_path_or_create(path)?;
let deser = DeSer::default();
if !exists {
let ser = deser.serialize(&data).context(ErrorKind::Serialization)?;
let ser = deser.serialize(&data)?;
backend.put_data(&ser)?;
}
@@ -1026,7 +1009,6 @@ where
#[cfg(test)]
mod tests {
use super::*;
use failure::Fail;
use std::collections::HashMap;
use tempfile::NamedTempFile;
@@ -1154,7 +1136,7 @@ mod tests {
panic!("Panic should be catched")
})
.expect_err("Did not error on panic in safe write!");
assert_eq!(ErrorKind::WritePanic, err.kind());
assert!(matches!(err, RustbreakError::WritePanic));
assert_eq!(
"Hello World",
@@ -1436,13 +1418,11 @@ mod tests {
file_path.push("rustbreak_path_db.db");
let err = TestDb::<PathBackend>::load_from_path(file_path)
.expect_err("should fail with file not found");
assert_eq!(ErrorKind::Backend, err.kind());
let io_err = err
.cause()
.expect("error has no cause")
.downcast_ref::<std::io::Error>()
.expect("error is not an io error");
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
if let RustbreakError::Backend(BackendError::Io(io_err)) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong error: {}", err)
};
dir.close().expect("Error while deleting temp directory!");
}
@@ -1455,13 +1435,11 @@ mod tests {
file_path.push("rustbreak_path_db.db");
let err = TestDb::<FileBackend>::load_from_path(file_path)
.expect_err("should fail with file not found");
assert_eq!(ErrorKind::Backend, err.kind());
let io_err = err
.cause()
.expect("error has no cause")
.downcast_ref::<std::io::Error>()
.expect("error is not an io error");
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
if let RustbreakError::Backend(BackendError::Io(io_err)) = &err {
assert_eq!(std::io::ErrorKind::NotFound, io_err.kind());
} else {
panic!("Wrong error: {}", err)
};
dir.close().expect("Error while deleting temp directory!");
}