diff options
Diffstat (limited to 'lib/core/libimagerror/src/error_gen.rs')
-rw-r--r-- | lib/core/libimagerror/src/error_gen.rs | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/lib/core/libimagerror/src/error_gen.rs b/lib/core/libimagerror/src/error_gen.rs new file mode 100644 index 00000000..ccfd1575 --- /dev/null +++ b/lib/core/libimagerror/src/error_gen.rs @@ -0,0 +1,440 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +#[macro_export] +macro_rules! generate_error_imports { + () => { + use std::error::Error; + use std::fmt::Error as FmtError; + use std::fmt::{Display, Formatter}; + + use $crate::into::IntoError; + } +} + +#[macro_export] +macro_rules! generate_error_module { + ( $exprs:item ) => { + pub mod error { + generate_error_imports!(); + $exprs + } + } +} + +#[macro_export] +macro_rules! generate_custom_error_types { + { + $name: ident, + $kindname: ident, + $customMemberTypeName: ident, + $($kind:ident => $string:expr),* + } => { + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum $kindname { + $( $kind ),* + } + + impl Display for $kindname { + + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + let s = match *self { + $( $kindname::$kind => $string ),* + }; + try!(write!(fmt, "{}", s)); + Ok(()) + } + + } + + impl IntoError for $kindname { + type Target = $name; + + fn into_error(self) -> Self::Target { + $name::new(self, None) + } + + fn into_error_with_cause(self, cause: Box<Error>) -> Self::Target { + $name::new(self, Some(cause)) + } + + } + + #[derive(Debug)] + pub struct $name { + err_type: $kindname, + cause: Option<Box<Error>>, + custom_data: Option<$customMemberTypeName>, + } + + impl $name { + + pub fn new(errtype: $kindname, cause: Option<Box<Error>>) -> $name { + $name { + err_type: errtype, + cause: cause, + custom_data: None, + } + } + + #[allow(dead_code)] + pub fn err_type(&self) -> $kindname { + self.err_type + } + + #[allow(dead_code)] + pub fn with_custom_data(mut self, custom: $customMemberTypeName) -> $name { + self.custom_data = Some(custom); + self + } + + } + + impl Into<$name> for $kindname { + + fn into(self) -> $name { + $name::new(self, None) + } + + } + + impl Display for $name { + + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + try!(write!(fmt, "[{}]", self.err_type)); + match self.custom_data { + Some(ref c) => write!(fmt, "{}", c), + None => Ok(()), + } + } + + } + + impl Error for $name { + + fn description(&self) -> &str { + match self.err_type { + $( $kindname::$kind => $string ),* + } + } + + fn cause(&self) -> Option<&Error> { + self.cause.as_ref().map(|e| &**e) + } + + } + + } +} + +#[macro_export] +macro_rules! generate_result_helper { + ( + $name: ident, + $kindname: ident + ) => { + /// Trait to replace + /// + /// ```ignore + /// foo.map_err(Box::new).map_err(|e| SomeType::SomeErrorKind.into_error_with_cause(e)) + /// // or: + /// foo.map_err(|e| SomeType::SomeErrorKind.into_error_with_cause(Box::new(e))) + /// ``` + /// + /// with much nicer + /// + /// ```ignore + /// foo.map_err_into(SomeType::SomeErrorKind) + /// ``` + /// + pub trait MapErrInto<T> { + fn map_err_into(self, error_kind: $kindname) -> Result<T, $name>; + } + + impl<T, E: Error + 'static> MapErrInto<T> for Result<T, E> { + + fn map_err_into(self, error_kind: $kindname) -> Result<T, $name> { + self.map_err(Box::new) + .map_err(|e| error_kind.into_error_with_cause(e)) + } + + } + } +} + +#[macro_export] +macro_rules! generate_option_helper { + ( + $name: ident, + $kindname: ident + ) => { + /// Trait to replace + /// + /// ```ignore + /// foo.ok_or(SomeType::SomeErrorKind.into_error()) + /// ``` + /// + /// with + /// + /// ```ignore + /// foo.ok_or_errkind(SomeType::SomeErrorKind) + /// ``` + pub trait OkOrErr<T> { + fn ok_or_errkind(self, kind: $kindname) -> Result<T, $name>; + } + + impl<T> OkOrErr<T> for Option<T> { + + fn ok_or_errkind(self, kind: $kindname) -> Result<T, $name> { + self.ok_or(kind.into_error()) + } + + } + } +} + +#[macro_export] +macro_rules! generate_error_types { + ( + $name: ident, + $kindname: ident, + $($kind:ident => $string:expr),* + ) => { + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)] + pub struct SomeNotExistingTypeWithATypeNameNoOneWillEverChoose {} + + impl Display for SomeNotExistingTypeWithATypeNameNoOneWillEverChoose { + fn fmt(&self, _: &mut Formatter) -> Result<(), FmtError> { + Ok(()) + } + } + + generate_custom_error_types!($name, $kindname, + SomeNotExistingTypeWithATypeNameNoOneWillEverChoose, + $($kind => $string),*); + + generate_result_helper!($name, $kindname); + generate_option_helper!($name, $kindname); + } +} + + +#[cfg(test)] +#[allow(dead_code)] +mod test { + + generate_error_module!( + generate_error_types!(TestError, TestErrorKind, + TestErrorKindA => "testerrorkind a", + TestErrorKindB => "testerrorkind B"); + ); + + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Copy)] + pub struct CustomData { + pub test: i32, + pub othr: i64, + } + + impl Display for CustomData { + fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { + Ok(()) + } + } + + generate_error_imports!(); + + #[allow(dead_code)] + generate_custom_error_types!(CustomTestError, CustomTestErrorKind, + CustomData, + CustomErrorKindA => "customerrorkind a", + CustomErrorKindB => "customerrorkind B"); + + // Allow dead code here. + // We wrote this to show that custom test types can be implemented. + #[allow(dead_code)] + impl CustomTestError { + pub fn test(&self) -> i32 { + match self.custom_data { + Some(t) => t.test, + None => 0, + } + } + + pub fn bar(&self) -> i64 { + match self.custom_data { + Some(t) => t.othr, + None => 0, + } + } + } + + + #[test] + fn test_a() { + use self::error::{TestError, TestErrorKind}; + + let kind = TestErrorKind::TestErrorKindA; + assert_eq!(String::from("testerrorkind a"), format!("{}", kind)); + + let e = TestError::new(kind, None); + assert_eq!(String::from("[testerrorkind a]"), format!("{}", e)); + } + + #[test] + fn test_b() { + use self::error::{TestError, TestErrorKind}; + + let kind = TestErrorKind::TestErrorKindB; + assert_eq!(String::from("testerrorkind B"), format!("{}", kind)); + + let e = TestError::new(kind, None); + assert_eq!(String::from("[testerrorkind B]"), format!("{}", e)); + + } + + #[test] + fn test_ab() { + use std::error::Error; + use self::error::{TestError, TestErrorKind}; + + let kinda = TestErrorKind::TestErrorKindA; + let kindb = TestErrorKind::TestErrorKindB; + assert_eq!(String::from("testerrorkind a"), format!("{}", kinda)); + assert_eq!(String::from("testerrorkind B"), format!("{}", kindb)); + + let e = TestError::new(kinda, Some(Box::new(TestError::new(kindb, None)))); + assert_eq!(String::from("[testerrorkind a]"), format!("{}", e)); + assert_eq!(TestErrorKind::TestErrorKindA, e.err_type()); + assert_eq!(String::from("[testerrorkind B]"), format!("{}", e.cause().unwrap())); + } + + pub mod anothererrormod { + generate_error_imports!(); + generate_error_types!(TestError, TestErrorKind, + TestErrorKindA => "testerrorkind a", + TestErrorKindB => "testerrorkind B"); + } + + #[test] + fn test_other_a() { + use self::anothererrormod::{TestError, TestErrorKind}; + + let kind = TestErrorKind::TestErrorKindA; + assert_eq!(String::from("testerrorkind a"), format!("{}", kind)); + + let e = TestError::new(kind, None); + assert_eq!(String::from("[testerrorkind a]"), format!("{}", e)); + } + + #[test] + fn test_other_b() { + use self::anothererrormod::{TestError, TestErrorKind}; + + let kind = TestErrorKind::TestErrorKindB; + assert_eq!(String::from("testerrorkind B"), format!("{}", kind)); + + let e = TestError::new(kind, None); + assert_eq!(String::from("[testerrorkind B]"), format!("{}", e)); + + } + + #[test] + fn test_other_ab() { + use std::error::Error; + use self::anothererrormod::{TestError, TestErrorKind}; + + let kinda = TestErrorKind::TestErrorKindA; + let kindb = TestErrorKind::TestErrorKindB; + assert_eq!(String::from("testerrorkind a"), format!("{}", kinda)); + assert_eq!(String::from("testerrorkind B"), format!("{}", kindb)); + + let e = TestError::new(kinda, Some(Box::new(TestError::new(kindb, None)))); + assert_eq!(String::from("[testerrorkind a]"), format!("{}", e)); + assert_eq!(TestErrorKind::TestErrorKindA, e.err_type()); + assert_eq!(String::from("[testerrorkind B]"), format!("{}", e.cause().unwrap())); + } + + #[test] + fn test_error_kind_mapping() { + use std::io::{Error, ErrorKind}; + use self::error::MapErrInto; + use self::error::TestErrorKind; + + let err : Result<(), _> = Err(Error::new(ErrorKind::Other, "")); + let err : Result<(), _> = err.map_err_into(TestErrorKind::TestErrorKindA); + + assert!(err.is_err()); + let err = err.unwrap_err(); + + match err.err_type() { + TestErrorKind::TestErrorKindA => assert!(true), + _ => assert!(false), + } + } + + #[test] + fn test_error_kind_double_mapping() { + use std::io::{Error, ErrorKind}; + use self::error::MapErrInto; + use self::error::TestErrorKind; + + let err : Result<(), _> = Err(Error::new(ErrorKind::Other, "")); + let err : Result<(), _> = err.map_err_into(TestErrorKind::TestErrorKindA) + .map_err_into(TestErrorKind::TestErrorKindB); + + assert!(err.is_err()); + let err = err.unwrap_err(); + match err.err_type() { + TestErrorKind::TestErrorKindB => assert!(true), + _ => assert!(false), + } + + // not sure how to test that the inner error is of TestErrorKindA, actually... + match err.cause() { + Some(_) => assert!(true), + None => assert!(false), + } + + } + + #[test] + fn test_error_option_good() { + use self::error::OkOrErr; + use self::error::TestErrorKind; + + let something = Some(1); + match something.ok_or_errkind(TestErrorKind::TestErrorKindA) { + Ok(1) => assert!(true), + _ => assert!(false), + } + } + + #[test] + fn test_error_option_bad() { + use self::error::OkOrErr; + use self::error::TestErrorKind; + + let something : Option<i32> = None; + match something.ok_or_errkind(TestErrorKind::TestErrorKindA) { + Ok(_) => assert!(false), + Err(_) => assert!(true), + } + } + +} |