use std::convert::Infallible;
use std::fmt;
use std::time::Duration;
use crate::{Kind, SurrealValue, ToSql, Value};
#[allow(missing_docs)]
mod code {
pub const PARSE_ERROR: i64 = -32700;
pub const INVALID_REQUEST: i64 = -32600;
pub const METHOD_NOT_FOUND: i64 = -32601;
pub const METHOD_NOT_ALLOWED: i64 = -32602;
pub const INVALID_PARAMS: i64 = -32603;
pub const LIVE_QUERY_NOT_SUPPORTED: i64 = -32604;
pub const BAD_LIVE_QUERY_CONFIG: i64 = -32605;
pub const BAD_GRAPHQL_CONFIG: i64 = -32606;
pub const INTERNAL_ERROR: i64 = -32000;
pub const CLIENT_SIDE_ERROR: i64 = -32001;
pub const INVALID_AUTH: i64 = -32002;
pub const QUERY_NOT_EXECUTED: i64 = -32003;
pub const QUERY_TIMEDOUT: i64 = -32004;
pub const QUERY_CANCELLED: i64 = -32005;
pub const QUERY_TRANSACTION_CONFLICT: i64 = -32009;
pub const THROWN: i64 = -32006;
pub const SERIALIZATION_ERROR: i64 = -32007;
pub const DESERIALIZATION_ERROR: i64 = -32008;
}
fn default_code() -> i64 {
code::INTERNAL_ERROR
}
#[derive(Debug, Clone, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
pub struct Error {
#[surreal(default = "default_code")]
code: i64,
message: String,
#[surreal(flatten)]
details: ErrorDetails,
#[surreal(default)]
cause: Option<Box<Error>>,
}
impl Error {
pub fn validation(message: String, details: impl Into<Option<ValidationError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.map(|d| match d {
ValidationError::Parse => code::PARSE_ERROR,
ValidationError::InvalidRequest => code::INVALID_REQUEST,
ValidationError::InvalidParams => code::INVALID_PARAMS,
ValidationError::NamespaceEmpty
| ValidationError::DatabaseEmpty
| ValidationError::InvalidParameter {
..
}
| ValidationError::InvalidContent {
..
}
| ValidationError::InvalidMerge {
..
} => code::INVALID_REQUEST,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::Validation(details),
cause: None,
}
}
pub fn not_allowed(message: String, details: impl Into<Option<NotAllowedError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.map(|d| match d {
NotAllowedError::Auth(auth_error) => match auth_error {
AuthError::TokenExpired => code::INVALID_AUTH,
AuthError::SessionExpired => code::INTERNAL_ERROR,
AuthError::InvalidAuth
| AuthError::UnexpectedAuth
| AuthError::MissingUserOrPass
| AuthError::NoSigninTarget
| AuthError::InvalidPass
| AuthError::TokenMakingFailed
| AuthError::InvalidRole {
..
}
| AuthError::NotAllowed {
..
}
| AuthError::InvalidSignup => code::INVALID_AUTH,
},
NotAllowedError::Method {
..
}
| NotAllowedError::Scripting
| NotAllowedError::Function {
..
}
| NotAllowedError::Target {
..
} => code::METHOD_NOT_ALLOWED,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::NotAllowed(details),
cause: None,
}
}
pub fn configuration(message: String, details: impl Into<Option<ConfigurationError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.map(|d| match d {
ConfigurationError::LiveQueryNotSupported => code::LIVE_QUERY_NOT_SUPPORTED,
ConfigurationError::BadLiveQueryConfig => code::BAD_LIVE_QUERY_CONFIG,
ConfigurationError::BadGraphqlConfig => code::BAD_GRAPHQL_CONFIG,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::Configuration(details),
cause: None,
}
}
pub fn thrown(message: String) -> Self {
Self {
message,
code: code::THROWN,
details: ErrorDetails::Thrown,
cause: None,
}
}
pub fn query(message: String, details: impl Into<Option<QueryError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.map(|d| match d {
QueryError::NotExecuted => code::QUERY_NOT_EXECUTED,
QueryError::TimedOut {
..
} => code::QUERY_TIMEDOUT,
QueryError::Cancelled => code::QUERY_CANCELLED,
QueryError::TransactionConflict => code::QUERY_TRANSACTION_CONFLICT,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::Query(details),
cause: None,
}
}
pub fn serialization(message: String, details: impl Into<Option<SerializationError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.map(|d| match d {
SerializationError::Serialization => code::SERIALIZATION_ERROR,
SerializationError::Deserialization => code::DESERIALIZATION_ERROR,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::Serialization(details),
cause: None,
}
}
pub fn not_found(message: String, details: impl Into<Option<NotFoundError>>) -> Self {
let details = details.into();
let code = details
.as_ref()
.and_then(|d| match d {
NotFoundError::Method {
..
} => Some(code::METHOD_NOT_FOUND),
_ => None,
})
.unwrap_or(code::INTERNAL_ERROR);
Self {
message,
code,
details: ErrorDetails::NotFound(details),
cause: None,
}
}
pub fn already_exists(message: String, details: impl Into<Option<AlreadyExistsError>>) -> Self {
let details = details.into();
Self {
message,
code: code::INTERNAL_ERROR,
details: ErrorDetails::AlreadyExists(details),
cause: None,
}
}
pub fn connection(message: String, details: impl Into<Option<ConnectionError>>) -> Self {
let details = details.into();
Self {
message,
code: code::CLIENT_SIDE_ERROR,
details: ErrorDetails::Connection(details),
cause: None,
}
}
pub fn internal(message: String) -> Self {
Self {
message,
code: code::INTERNAL_ERROR,
details: ErrorDetails::Internal,
cause: None,
}
}
fn context(message: String) -> Self {
Self {
code: code::INTERNAL_ERROR,
message,
details: ErrorDetails::Context,
cause: None,
}
}
#[doc(hidden)]
pub fn from_details(message: String, details: ErrorDetails) -> Self {
Self {
code: default_code(),
message,
details,
cause: None,
}
}
#[doc(hidden)]
pub fn from_details_with_cause(
message: String,
details: ErrorDetails,
cause: Option<Box<Error>>,
) -> Self {
Self {
code: default_code(),
message,
details,
cause,
}
}
#[doc(hidden)]
pub fn from_parts(message: String, kind: Option<&str>, details: Option<Value>) -> Self {
let kind_str = kind.unwrap_or("Internal");
let typed_details = match details {
Some(v) => ErrorDetails::from_value_with_kind_str(kind_str, v)
.unwrap_or_else(|_| ErrorDetails::from_kind_str(kind_str)),
None => ErrorDetails::from_kind_str(kind_str),
};
Self {
code: default_code(),
message,
details: typed_details,
cause: None,
}
}
pub fn cause(&self) -> Option<&Error> {
self.cause.as_deref()
}
pub fn with_cause(mut self, cause: Error) -> Self {
self.cause = Some(Box::new(cause));
self
}
#[doc(hidden)]
#[allow(clippy::needless_pass_by_value)] pub fn from_anyhow_with_chain(error: anyhow::Error) -> Self {
Self::internal(error.to_string())
}
pub fn kind_str(&self) -> &'static str {
self.details.kind_str()
}
pub fn message(&self) -> &str {
&self.message
}
pub fn details(&self) -> &ErrorDetails {
&self.details
}
pub fn is_validation(&self) -> bool {
self.details.is_validation()
}
pub fn is_configuration(&self) -> bool {
self.details.is_configuration()
}
pub fn is_query(&self) -> bool {
self.details.is_query()
}
pub fn is_serialization(&self) -> bool {
self.details.is_serialization()
}
pub fn is_not_allowed(&self) -> bool {
self.details.is_not_allowed()
}
pub fn is_not_found(&self) -> bool {
self.details.is_not_found()
}
pub fn is_already_exists(&self) -> bool {
self.details.is_already_exists()
}
pub fn is_connection(&self) -> bool {
self.details.is_connection()
}
pub fn is_thrown(&self) -> bool {
self.details.is_thrown()
}
pub fn is_internal(&self) -> bool {
self.details.is_internal()
}
pub fn is_context(&self) -> bool {
self.details.is_context()
}
pub fn validation_details(&self) -> Option<&ValidationError> {
match &self.details {
ErrorDetails::Validation(d) => d.as_ref(),
_ => None,
}
}
pub fn not_allowed_details(&self) -> Option<&NotAllowedError> {
match &self.details {
ErrorDetails::NotAllowed(d) => d.as_ref(),
_ => None,
}
}
pub fn configuration_details(&self) -> Option<&ConfigurationError> {
match &self.details {
ErrorDetails::Configuration(d) => d.as_ref(),
_ => None,
}
}
pub fn serialization_details(&self) -> Option<&SerializationError> {
match &self.details {
ErrorDetails::Serialization(d) => d.as_ref(),
_ => None,
}
}
pub fn not_found_details(&self) -> Option<&NotFoundError> {
match &self.details {
ErrorDetails::NotFound(d) => d.as_ref(),
_ => None,
}
}
pub fn query_details(&self) -> Option<&QueryError> {
match &self.details {
ErrorDetails::Query(d) => d.as_ref(),
_ => None,
}
}
pub fn already_exists_details(&self) -> Option<&AlreadyExistsError> {
match &self.details {
ErrorDetails::AlreadyExists(d) => d.as_ref(),
_ => None,
}
}
pub fn connection_details(&self) -> Option<&ConnectionError> {
match &self.details {
ErrorDetails::Connection(d) => d.as_ref(),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[non_exhaustive]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details", skip_content_if = "Value::is_empty")]
pub enum ErrorDetails {
Validation(Option<ValidationError>),
Configuration(Option<ConfigurationError>),
Query(Option<QueryError>),
Serialization(Option<SerializationError>),
NotAllowed(Option<NotAllowedError>),
NotFound(Option<NotFoundError>),
AlreadyExists(Option<AlreadyExistsError>),
Connection(Option<ConnectionError>),
Thrown,
#[surreal(other)]
Internal,
Context,
}
impl ErrorDetails {
pub fn kind_str(&self) -> &'static str {
match self {
Self::Validation(_) => "Validation",
Self::Configuration(_) => "Configuration",
Self::Query(_) => "Query",
Self::Serialization(_) => "Serialization",
Self::NotAllowed(_) => "NotAllowed",
Self::NotFound(_) => "NotFound",
Self::AlreadyExists(_) => "AlreadyExists",
Self::Connection(_) => "Connection",
Self::Thrown => "Thrown",
Self::Internal => "Internal",
Self::Context => "Context",
}
}
pub fn is_validation(&self) -> bool {
matches!(self, Self::Validation(_))
}
pub fn is_configuration(&self) -> bool {
matches!(self, Self::Configuration(_))
}
pub fn is_query(&self) -> bool {
matches!(self, Self::Query(_))
}
pub fn is_serialization(&self) -> bool {
matches!(self, Self::Serialization(_))
}
pub fn is_not_allowed(&self) -> bool {
matches!(self, Self::NotAllowed(_))
}
pub fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound(_))
}
pub fn is_already_exists(&self) -> bool {
matches!(self, Self::AlreadyExists(_))
}
pub fn is_connection(&self) -> bool {
matches!(self, Self::Connection(_))
}
pub fn is_thrown(&self) -> bool {
matches!(self, Self::Thrown)
}
pub fn is_internal(&self) -> bool {
matches!(self, Self::Internal)
}
pub fn is_context(&self) -> bool {
matches!(self, Self::Context)
}
pub(crate) fn from_kind_str(kind: &str) -> Self {
match kind {
"Validation" => Self::Validation(None),
"Configuration" => Self::Configuration(None),
"Query" => Self::Query(None),
"Serialization" => Self::Serialization(None),
"NotAllowed" => Self::NotAllowed(None),
"NotFound" => Self::NotFound(None),
"AlreadyExists" => Self::AlreadyExists(None),
"Connection" => Self::Connection(None),
"Thrown" => Self::Thrown,
"Context" => Self::Context,
_ => Self::Internal,
}
}
pub(crate) fn from_value_with_kind_str(kind: &str, value: Value) -> Result<Self, Error> {
match kind {
"Validation" => {
ValidationError::from_value(value).map(|v| ErrorDetails::Validation(Some(v)))
}
"Configuration" => {
ConfigurationError::from_value(value).map(|v| ErrorDetails::Configuration(Some(v)))
}
"Query" => QueryError::from_value(value).map(|v| ErrorDetails::Query(Some(v))),
"Serialization" => {
SerializationError::from_value(value).map(|v| ErrorDetails::Serialization(Some(v)))
}
"NotAllowed" => {
NotAllowedError::from_value(value).map(|v| ErrorDetails::NotAllowed(Some(v)))
}
"NotFound" => NotFoundError::from_value(value).map(|v| ErrorDetails::NotFound(Some(v))),
"AlreadyExists" => {
AlreadyExistsError::from_value(value).map(|v| ErrorDetails::AlreadyExists(Some(v)))
}
"Connection" => {
ConnectionError::from_value(value).map(|v| ErrorDetails::Connection(Some(v)))
}
"Thrown" => Ok(Self::Thrown),
"Context" => Ok(Self::Context),
_ => Ok(Self::Internal),
}
}
pub fn has_details(&self) -> bool {
match self {
Self::Validation(d) => d.is_some(),
Self::Configuration(d) => d.is_some(),
Self::Query(d) => d.is_some(),
Self::Serialization(d) => d.is_some(),
Self::NotAllowed(d) => d.is_some(),
Self::NotFound(d) => d.is_some(),
Self::AlreadyExists(d) => d.is_some(),
Self::Connection(d) => d.is_some(),
Self::Thrown | Self::Internal | Self::Context => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum AuthError {
#[surreal(skip_content)]
TokenExpired,
#[surreal(skip_content)]
SessionExpired,
#[surreal(skip_content)]
InvalidAuth,
#[surreal(skip_content)]
UnexpectedAuth,
#[surreal(skip_content)]
MissingUserOrPass,
#[surreal(skip_content)]
NoSigninTarget,
#[surreal(skip_content)]
InvalidPass,
#[surreal(skip_content)]
TokenMakingFailed,
#[surreal(skip_content)]
InvalidSignup,
InvalidRole {
name: String,
},
NotAllowed {
actor: String,
action: String,
resource: String,
},
}
impl From<AuthError> for Option<NotAllowedError> {
fn from(auth_error: AuthError) -> Self {
Some(NotAllowedError::Auth(auth_error))
}
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum ValidationError {
#[surreal(skip_content)]
Parse,
#[surreal(skip_content)]
InvalidRequest,
#[surreal(skip_content)]
InvalidParams,
#[surreal(skip_content)]
NamespaceEmpty,
#[surreal(skip_content)]
DatabaseEmpty,
InvalidParameter {
name: String,
},
InvalidContent {
value: String,
},
InvalidMerge {
value: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum NotAllowedError {
#[surreal(skip_content)]
Scripting,
Auth(AuthError),
Method {
name: String,
},
Function {
name: String,
},
Target {
name: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", skip_content)]
#[non_exhaustive]
pub enum ConfigurationError {
LiveQueryNotSupported,
BadLiveQueryConfig,
BadGraphqlConfig,
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", skip_content)]
#[non_exhaustive]
pub enum SerializationError {
Serialization,
Deserialization,
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum NotFoundError {
Method {
name: String,
},
Session {
id: Option<String>,
},
Table {
name: String,
},
Record {
id: String,
},
Namespace {
name: String,
},
Database {
name: String,
},
#[surreal(skip_content)]
Transaction,
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum QueryError {
#[surreal(skip_content)]
NotExecuted,
TimedOut {
duration: Duration,
},
#[surreal(skip_content)]
Cancelled,
#[surreal(skip_content)]
TransactionConflict,
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", content = "details")]
#[non_exhaustive]
pub enum AlreadyExistsError {
Session {
id: String,
},
Table {
name: String,
},
Record {
id: String,
},
Namespace {
name: String,
},
Database {
name: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, SurrealValue)]
#[surreal(crate = "crate")]
#[surreal(tag = "kind", skip_content)]
#[non_exhaustive]
pub enum ConnectionError {
Uninitialised,
AlreadyConnected,
ConnectionFailed,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause.as_deref().map(|e| e as &(dyn std::error::Error + 'static))
}
}
pub trait Chain<T, E> {
fn chain<C>(self, context: C) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static;
fn chain_with<C, F>(self, f: F) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C;
}
impl<T, E> Chain<T, E> for Result<T, E>
where
E: Into<Error>,
{
fn chain<C>(self, context: C) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
{
self.map_err(|e| Error::context(context.to_string()).with_cause(e.into()))
}
fn chain_with<C, F>(self, f: F) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
self.map_err(|e| Error::context(f().to_string()).with_cause(e.into()))
}
}
impl<T> Chain<T, Infallible> for Option<T> {
fn chain<C>(self, context: C) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
{
self.ok_or_else(|| Error::context(context.to_string()))
}
fn chain_with<C, F>(self, f: F) -> Result<T, Error>
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
self.ok_or_else(|| Error::context(f().to_string()))
}
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
use std::io::ErrorKind;
let msg = error.to_string();
match error.kind() {
ErrorKind::NotFound => Error::not_found(msg, None),
ErrorKind::AlreadyExists => Error::already_exists(msg, None),
ErrorKind::PermissionDenied
| ErrorKind::ReadOnlyFilesystem
| ErrorKind::ResourceBusy
| ErrorKind::ExecutableFileBusy => Error::not_allowed(msg, None),
ErrorKind::ConnectionRefused
| ErrorKind::ConnectionReset
| ErrorKind::ConnectionAborted
| ErrorKind::NotConnected
| ErrorKind::HostUnreachable
| ErrorKind::NetworkUnreachable
| ErrorKind::NetworkDown
| ErrorKind::TimedOut
| ErrorKind::BrokenPipe
| ErrorKind::AddrInUse
| ErrorKind::AddrNotAvailable
| ErrorKind::StaleNetworkFileHandle => Error::connection(msg, ConnectionError::ConnectionFailed),
ErrorKind::InvalidData | ErrorKind::UnexpectedEof => {
Error::serialization(msg, SerializationError::Deserialization)
}
ErrorKind::InvalidInput
| ErrorKind::InvalidFilename
| ErrorKind::NotADirectory
| ErrorKind::IsADirectory
| ErrorKind::DirectoryNotEmpty => Error::validation(msg, None),
_ => Error::internal(msg),
}
}
}
#[cfg(feature = "convert")]
impl From<async_channel::RecvError> for Error {
fn from(error: async_channel::RecvError) -> Self {
Error::connection(
format!("Channel receive error: {}", error),
ConnectionError::ConnectionFailed,
)
}
}
#[cfg(feature = "convert")]
impl From<url::ParseError> for Error {
fn from(error: url::ParseError) -> Self {
Error::validation(error.to_string(), ValidationError::InvalidRequest)
}
}
#[cfg(feature = "convert")]
impl From<semver::Error> for Error {
fn from(error: semver::Error) -> Self {
Error::validation(error.to_string(), ValidationError::InvalidParams)
}
}
#[cfg(feature = "reqwest")]
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Error::connection(error.to_string(), ConnectionError::ConnectionFailed)
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TypeError {
Conversion(ConversionError),
OutOfRange(OutOfRangeError),
LengthMismatch(LengthMismatchError),
Invalid(String),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ConversionError {
pub expected: Kind,
pub actual: Kind,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct OutOfRangeError {
pub value: String,
pub target_type: String,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct LengthMismatchError {
pub expected: usize,
pub actual: usize,
pub target_type: String,
}
impl ConversionError {
pub fn new(expected: Kind, actual: Kind) -> Self {
Self {
expected,
actual,
context: None,
}
}
pub fn from_value(expected: Kind, value: &Value) -> Self {
Self {
expected,
actual: value.kind(),
context: None,
}
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
}
impl OutOfRangeError {
pub fn new(value: impl fmt::Display, target_type: impl Into<String>) -> Self {
Self {
value: value.to_string(),
target_type: target_type.into(),
context: None,
}
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
}
impl LengthMismatchError {
pub fn new(expected: usize, actual: usize, target_type: impl Into<String>) -> Self {
Self {
expected,
actual,
target_type: target_type.into(),
}
}
}
impl fmt::Display for TypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeError::Conversion(e) => write!(f, "{e}"),
TypeError::OutOfRange(e) => write!(f, "{e}"),
TypeError::LengthMismatch(e) => write!(f, "{e}"),
TypeError::Invalid(e) => write!(f, "Invalid: {e}"),
}
}
}
impl fmt::Display for ConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Expected {}, got {}", self.expected.to_sql(), self.actual.to_sql())?;
if let Some(context) = &self.context {
write!(f, " ({})", context)?;
}
Ok(())
}
}
impl fmt::Display for OutOfRangeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Value {} is out of range for type {}", self.value, self.target_type)?;
if let Some(context) = &self.context {
write!(f, " ({})", context)?;
}
Ok(())
}
}
impl fmt::Display for LengthMismatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Length mismatch for {}: expected {}, got {}",
self.target_type, self.expected, self.actual
)
}
}
impl std::error::Error for TypeError {}
impl std::error::Error for ConversionError {}
impl std::error::Error for OutOfRangeError {}
impl std::error::Error for LengthMismatchError {}
impl From<ConversionError> for Error {
fn from(e: ConversionError) -> Self {
Error::internal(e.to_string())
}
}
impl From<OutOfRangeError> for Error {
fn from(e: OutOfRangeError) -> Self {
Error::internal(e.to_string())
}
}
impl From<LengthMismatchError> for Error {
fn from(e: LengthMismatchError) -> Self {
Error::internal(e.to_string())
}
}
impl From<TypeError> for Error {
fn from(e: TypeError) -> Self {
Error::internal(e.to_string())
}
}
pub fn conversion_error(expected: Kind, value: impl Into<Value>) -> Error {
let value = value.into();
ConversionError::from_value(expected, &value).into()
}
pub fn out_of_range_error(value: impl fmt::Display, target_type: impl Into<String>) -> Error {
OutOfRangeError::new(value, target_type).into()
}
pub fn length_mismatch_error(
expected: usize,
actual: usize,
target_type: impl Into<String>,
) -> Error {
LengthMismatchError::new(expected, actual, target_type).into()
}
pub fn union_conversion_error(expected: Kind, value: impl Into<Value>) -> Error {
let value = value.into();
ConversionError::from_value(expected, &value)
.with_context("Value does not match any variant in union type")
.into()
}