use {
	crate::{error, prelude::*},
	glib::variant::VariantTypeMismatchError,
	std::{
		convert::Infallible,
		error::Error as StdError,
		fmt,
		num::{ParseIntError, TryFromIntError},
		str::Utf8Error,
	},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LuaError {
	Custom(String),
	Glib(Error),
	Conversion(TryFromIntError),
	Parse(ParseIntError),
	Utf8(Utf8Error),
	TypeMismatch(VariantTypeMismatchError),
	UnsupportedType(Cow<'static, VariantTy>),
	LengthMismatch { actual: usize, expected: usize },
}
impl From<TryFromIntError> for LuaError {
	fn from(e: TryFromIntError) -> Self {
		LuaError::Conversion(e)
	}
}
impl From<ParseIntError> for LuaError {
	fn from(e: ParseIntError) -> Self {
		LuaError::Parse(e)
	}
}
impl From<Utf8Error> for LuaError {
	fn from(e: Utf8Error) -> Self {
		LuaError::Utf8(e)
	}
}
impl From<VariantTypeMismatchError> for LuaError {
	fn from(e: VariantTypeMismatchError) -> Self {
		LuaError::TypeMismatch(e)
	}
}
impl From<Error> for LuaError {
	fn from(e: Error) -> Self {
		LuaError::Glib(e)
	}
}
impl From<Infallible> for LuaError {
	fn from(v: Infallible) -> Self {
		match v {}
	}
}
impl From<LuaError> for Error {
	fn from(e: LuaError) -> Error {
		match e {
			LuaError::Glib(e) => e,
			LuaError::Custom(e) => error::operation_failed(e),
			e => error::invalid_argument(e),
		}
	}
}
impl fmt::Display for LuaError {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			LuaError::Custom(e) => fmt::Display::fmt(e, f),
			LuaError::Glib(e) => fmt::Display::fmt(e, f),
			LuaError::Conversion(e) => fmt::Display::fmt(e, f),
			LuaError::Parse(e) => fmt::Display::fmt(e, f),
			LuaError::Utf8(e) => fmt::Display::fmt(e, f),
			LuaError::TypeMismatch(e) => fmt::Display::fmt(e, f),
			LuaError::UnsupportedType(t) => write!(f, "type {t} is not supported by lua"),
			LuaError::LengthMismatch { actual, expected } => write!(f, "invalid length {actual}, expected {expected}"),
		}
	}
}
impl StdError for LuaError {
	fn source(&self) -> Option<&(dyn StdError + 'static)> {
		match self {
			LuaError::Glib(e) => Some(e),
			LuaError::Conversion(e) => Some(e),
			LuaError::Parse(e) => Some(e),
			LuaError::Utf8(e) => Some(e),
			LuaError::TypeMismatch(e) => Some(e),
			LuaError::Custom(..) | LuaError::UnsupportedType(..) | LuaError::LengthMismatch { .. } => None,
		}
	}
}
#[cfg(feature = "serde")]
impl serde::de::Error for LuaError {
	fn invalid_length(actual: usize, expected: &dyn serde::de::Expected) -> Self {
		let expected = expected.to_string();
		match expected.parse() {
			Ok(expected) => LuaError::LengthMismatch { actual, expected },
			_ => Self::custom(format_args!("invalid length {actual}, expected {expected}")),
		}
	}
	fn custom<T: Display>(msg: T) -> Self {
		LuaError::Custom(msg.to_string())
	}
}
#[cfg(feature = "serde")]
impl serde::ser::Error for LuaError {
	fn custom<T: Display>(msg: T) -> Self {
		LuaError::Custom(msg.to_string())
	}
}
#[cfg(feature = "serde")]
impl LuaError {
	pub(crate) fn serde_error<E: serde::de::Error>(self) -> E {
		E::custom(self)
	}
	pub(crate) fn serde_error_ser<E: serde::ser::Error>(self) -> E {
		E::custom(self)
	}
}