//! Error handling.

pub use failure::{Error, Fail};

use std::fmt;

macro_rules! ok {
	($v:expr, $msg:expr) => {
		match $v {
			Some(v) => Ok(v),
			None    => Err($crate::err::err_msg($msg)),
		}
	};
}

macro_rules! flag_ok {
	($t:ident$(::$tc:ident)*, $v:expr) => {
		{
			let v = $v;

			match $t$(::$tc)*::from_bits(v) {
				Some(v) => Ok(v),
				None    => Err($crate::err::ReprError::new(stringify!($t), v)),
			}
		}
	};
}

macro_rules! bail {
	($e:expr) => {
		return Err($crate::err::err_msg($e));
	};
}

#[macro_export]
macro_rules! backtrace {
	($e:expr) => {
		if cfg!(debug_assertions) {
			dbg!($e.backtrace());
		}
	}
}

/// Returns an `Error` with a static string.
///
/// # Examples
///
/// ```
/// use maraiah::err::err_msg;
///
/// assert_eq!(format!("{}", err_msg("oh no not things")),
///            "oh no not things");
/// ```
pub fn err_msg(msg: &'static str) -> Error {ErrMsg(msg).into()}

impl ParseEnumError
{
	/// Returns an `Error` with a message for enumeration parsing errata.
	///
	/// # Examples
	///
	/// ```
	/// use maraiah::err::ParseEnumError;
	///
	/// assert_eq!(format!("{}", ParseEnumError::new("TypeName")),
	///            "could not parse TypeName");
	/// ```
	#[inline]
	pub const fn new(t: &'static str) -> Self
	{
		Self(t)
	}
}

impl ParseFlagError
{
	/// Returns an `Error` with a message for flag parsing errata.
	///
	/// # Examples
	///
	/// ```
	/// use maraiah::err::ParseFlagError;
	///
	/// assert_eq!(format!("{}", ParseFlagError::new("TypeName")),
	///            "could not parse TypeName");
	/// ```
	#[inline]
	pub const fn new(t: &'static str) -> Self
	{
		Self(t)
	}
}

impl ReprError
{
	/// Returns an `Error` with a message for representation errata.
	///
	/// # Examples
	///
	/// ```
	/// use maraiah::err::ReprError;
	///
	/// assert_eq!(format!("{}", ReprError::new("TypeName", 77)),
	///            "bad TypeName (77)");
	/// ```
	pub fn new<T: Into<i64>>(t: &'static str, n: T) -> Self
	{
		Self(t, n.into())
	}
}

impl Fail for ErrMsg {}
impl Fail for ParseEnumError {}
impl Fail for ParseFlagError {}
impl Fail for ReprError {}

impl fmt::Display for ParseEnumError
{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
	{
		write!(f, "could not parse {}", self.0)
	}
}

impl fmt::Display for ParseFlagError
{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
	{
		write!(f, "could not parse {}", self.0)
	}
}

impl fmt::Display for ReprError
{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
	{
		write!(f, "bad {} ({})", self.0, self.1)
	}
}

impl fmt::Display for ErrMsg
{
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
	{
		f.write_str(self.0)
	}
}

#[derive(Clone, Debug)]
struct ErrMsg(&'static str);

/// A parser error for an enumeration.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParseEnumError(&'static str);

/// A parser error for a bit field.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ParseFlagError(&'static str);

/// A representation error for an integer.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct ReprError(&'static str, i64);

/// A generic `failure` based `Result` type.
pub type ResultS<T> = Result<T, Error>;

// EOF
