use core::fmt;
use std::fmt::Display;
use std::io::Error as IoError;
use std::string::FromUtf8Error;
use base64::DecodeError as Base64Error;
#[cfg(storage)]
use ext_sort::SortError;
use fst::Error as FstError;
use http::header::{InvalidHeaderName, InvalidHeaderValue, ToStrError};
use jsonwebtoken::errors::Error as JWTError;
use object_store::Error as ObjectStoreError;
use revision::Error as RevisionError;
use serde::Serialize;
use storekey::DecodeError as StorekeyError;
use surrealdb_types::ToSql;
use thiserror::Error;
use crate::api::err::ApiError;
use crate::buc::BucketOperation;
use crate::expr::operation::PatchError;
use crate::expr::{Expr, Idiom};
use crate::iam::Error as IamError;
use crate::idx::ft::MatchRef;
use crate::idx::trees::vector::SharedVector;
use crate::kvs::Error as KvsError;
use crate::syn::error::RenderedError as RenderedParserError;
use crate::val::{CastError, CoerceError, Duration, RecordId, TableName, Value};
mod to_types;
pub(crate) use to_types::into_types_error;
pub fn anyhow_to_types_error(error: anyhow::Error) -> surrealdb_types::Error {
match error.downcast::<Error>() {
Ok(e) => into_types_error(e),
Err(e) => surrealdb_types::Error::from_anyhow_with_chain(e),
}
}
pub fn is_query_cancelled(error: &anyhow::Error) -> bool {
matches!(error.downcast_ref::<Error>(), Some(Error::QueryCancelled))
}
pub fn is_query_timedout(error: &anyhow::Error) -> bool {
matches!(error.downcast_ref::<Error>(), Some(Error::QueryTimedout(_)))
}
#[derive(Error, Debug)]
#[allow(clippy::enum_variant_names)]
#[allow(dead_code, reason = "Some variants are only used by specific KV stores")]
pub(crate) enum Error {
#[error("The database encountered unreachable logic: {0}")]
Unreachable(String),
#[error("An error occurred: {0}")]
Thrown(String),
#[error("There was a problem with the key-value store: {0}")]
Kvs(#[from] KvsError),
#[error("Specify a namespace to use")]
NsEmpty,
#[error("Specify a database to use")]
DbEmpty,
#[error("Parse error: {0}")]
InvalidQuery(RenderedParserError),
#[error("Cannot use {} in a CONTENT clause", value.to_sql())]
InvalidContent {
value: Value,
},
#[error("Cannot use {} in a MERGE clause", value.to_sql())]
InvalidMerge {
value: Value,
},
#[error("The JSON Patch contains invalid operations. {0}")]
InvalidPatch(PatchError),
#[error("Invalid query: {message}")]
Query {
message: String,
},
#[error(
"Given test operation failed for JSON Patch. Expected `{expected}`, but got `{got}` instead."
)]
PatchTest {
expected: String,
got: String,
},
#[error("Remote HTTP request functions are not enabled")]
#[allow(dead_code)]
HttpDisabled,
#[error("'{name}' is a protected variable and cannot be set")]
InvalidParam {
name: String,
},
#[error("Found {} on FETCH CLAUSE, but FETCH expects an idiom, a string or fields", value.to_sql())]
InvalidFetch {
value: Expr,
},
#[error("Found {value} but the LIMIT clause must evaluate to a positive integer")]
InvalidLimit {
value: String,
},
#[error("Found {value} but the START clause must evaluate to a positive integer")]
InvalidStart {
value: String,
},
#[error("Problem with embedded script function. {message}")]
InvalidScript {
message: String,
},
#[error("Problem with machine learning computation. {message}")]
#[allow(dead_code)]
InvalidModel {
message: String,
},
#[error("There was a problem running the {name}() function. {message}")]
InvalidFunction {
name: String,
message: String,
},
#[error("Incorrect arguments for function {name}(). {message}")]
InvalidFunctionArguments {
name: String,
message: String,
},
#[error("Incorrect arguments for method {name}(). {message}")]
InvalidMethodArguments {
name: String,
message: String,
},
#[error("Invalid aggregation: {message}")]
InvalidAggregation {
message: String,
},
#[error(
"Incorrect selector for aggregate selection, expression `{expr}` within in selector cannot be aggregated in a group."
)]
InvalidAggregationSelector {
expr: String,
},
#[error("The URL `{0}` is invalid")]
#[cfg_attr(not(any(feature = "http", feature = "jwks")), expect(dead_code))]
InvalidUrl(String),
#[error("Incorrect vector dimension ({current}). Expected a vector of {expected} dimension.")]
InvalidVectorDimension {
current: usize,
expected: usize,
},
#[error(
"Unable to compute distance.The calculated result is not a valid number: {dist}. Vectors: {left:?} - {right:?}"
)]
InvalidVectorDistance {
left: SharedVector,
right: SharedVector,
dist: f64,
},
#[error("The value cannot be converted to a vector: {0}")]
InvalidVectorValue(String),
#[error("Invalid regular expression: {0:?}")]
InvalidRegex(String),
#[error("Invalid timeout: {0:?} seconds")]
InvalidTimeout(u64),
#[error("Invalid control flow statement, break or continue statement found outside of loop.")]
InvalidControlFlow,
#[error("The query was not executed because it exceeded the timeout: {0}")]
QueryTimedout(Duration),
#[error("The transaction was not completed because it exceeded the timeout: {0}")]
TransactionTimedout(Duration),
#[error("The query was not executed due to a cancelled transaction")]
QueryCancelled,
#[error("The query was not executed due to the memory threshold being reached")]
QueryBeyondMemoryThreshold,
#[error("The query was not executed due to a failed transaction. {message}")]
QueryNotExecuted {
message: String,
},
#[error("You don't have permission to change to the {ns} namespace")]
NsNotAllowed {
ns: String,
},
#[error("You don't have permission to change to the {db} database")]
DbNotAllowed {
db: String,
},
#[error("The namespace '{name}' does not exist")]
NsNotFound {
name: String,
},
#[error("The database '{name}' does not exist")]
DbNotFound {
name: String,
},
#[error("The event '{name}' does not exist")]
EvNotFound {
name: String,
},
#[error("The function '{name}' does not exist")]
FcNotFound {
name: String,
},
#[error("The module '{name}' does not exist")]
MdNotFound {
name: String,
},
#[error("The field '{name}' does not exist")]
FdNotFound {
name: String,
},
#[error("The model 'ml::{name}' does not exist")]
MlNotFound {
name: String,
},
#[error("The node '{uuid}' does not exist")]
NdNotFound {
uuid: String,
},
#[error("The param '${name}' does not exist")]
PaNotFound {
name: String,
},
#[error("The sequence '{name}' does not exist")]
SeqNotFound {
name: String,
},
#[error("The config for {name} does not exist")]
CgNotFound {
name: String,
},
#[error("The table '{name}' does not exist")]
TbNotFound {
name: TableName,
},
#[error("The api '{value}' does not exist")]
ApNotFound {
value: String,
},
#[error("The analyzer '{name}' does not exist")]
AzNotFound {
name: String,
},
#[error(
"The analyzer '{name}' is in use by index '{index}' on table '{table}' and cannot be removed"
)]
AzInUse {
name: String,
table: String,
index: String,
},
#[error("The bucket '{name}' does not exist")]
BuNotFound {
name: String,
},
#[error("The index '{name}' does not exist")]
IxNotFound {
name: String,
},
#[error("The record '{rid}' does not exist")]
IdNotFound {
rid: String,
},
#[error("The root user '{name}' does not exist")]
UserRootNotFound {
name: String,
},
#[error("The user '{name}' does not exist in the namespace '{ns}'")]
UserNsNotFound {
name: String,
ns: String,
},
#[error("The user '{name}' does not exist in the database '{db}'")]
UserDbNotFound {
name: String,
ns: String,
db: String,
},
#[error("Unable to perform the realtime query")]
RealtimeDisabled,
#[error("Reached excessive computation depth due to functions, subqueries, or computed values")]
ComputationDepthExceeded,
#[error("Invalid statement: {0}")]
InvalidStatement(String),
#[error("Cannot execute statement using value: {value}")]
InvalidStatementTarget {
value: String,
},
#[error("Cannot execute CREATE statement using value: {value}")]
CreateStatement {
value: String,
},
#[error("Cannot execute UPSERT statement using value: {value}")]
UpsertStatement {
value: String,
},
#[error("Cannot execute UPDATE statement using value: {value}")]
UpdateStatement {
value: String,
},
#[error("Cannot execute RELATE statement where property 'in' is: {value}")]
RelateStatementIn {
value: String,
},
#[error("Cannot execute RELATE statement where property 'id' is: {value}")]
RelateStatementId {
value: String,
},
#[error("Cannot execute RELATE statement where property 'out' is: {value}")]
RelateStatementOut {
value: String,
},
#[error("Cannot execute DELETE statement using value: {value}")]
DeleteStatement {
value: String,
},
#[error("Cannot execute INSERT statement using value: {value}")]
InsertStatement {
value: String,
},
#[error("Cannot execute INSERT statement where property 'in' is: {value}")]
InsertStatementIn {
value: String,
},
#[error("Cannot execute INSERT statement where property 'id' is: {value}")]
InsertStatementId {
value: String,
},
#[error("Cannot execute INSERT statement where property 'out' is: {value}")]
InsertStatementOut {
value: String,
},
#[error("Cannot execute LIVE statement using value: {value}")]
LiveStatement {
value: String,
},
#[error("Cannot execute KILL statement using id: {value}")]
KillStatement {
value: String,
},
#[error("Expected a single result output when using the ONLY keyword")]
SingleOnlyOutput,
#[error("You don't have permission to view the ${name} parameter")]
ParamPermissions {
name: String,
},
#[error("You don't have permission to run the {name} function")]
FunctionPermissions {
name: String,
},
#[error("You don't have permission to {op} this file in the `{name}` bucket")]
BucketPermissions {
name: String,
op: BucketOperation,
},
#[error("A PERMISSIONS clause cannot contain a statement that modifies data")]
PermissionPredicateSideEffect,
#[error(
"Found a non-read-only expression in the PERMISSIONS clause for {kind} `{name}`, but a PERMISSIONS clause must not modify data"
)]
PermissionClauseNotReadonly {
kind: &'static str,
name: String,
},
#[error("Database record `{record}` already exists", record = record.to_sql())]
RecordExists {
record: RecordId,
},
#[error("Database index `{index}` already contains {value}, with record `{record}`", record = record.to_sql())]
IndexExists {
record: RecordId,
index: String,
value: String,
},
#[error("Found record: `{record}` which is {}a relation, but expected a {target_type}", if *relation { "" } else { "not " })]
TableCheck {
record: String,
relation: bool,
target_type: String,
},
#[error(
"Cannot write to the `{table}` table, as it is a view (defined with `AS SELECT`); view tables are read-only and their records are computed from the source query"
)]
TableIsView {
table: String,
},
#[error(
"Found {value} for field `{field}`, with record `{record}`, but field must conform to: {check}",
field = field.to_sql()
)]
FieldValue {
record: String,
value: String,
field: Idiom,
check: String,
},
#[error("Tried to set `${name}`, but couldn't coerce value: {error}")]
SetCoerce {
name: String,
error: Box<CoerceError>,
},
#[error("Couldn't coerce return value from function `{name}`: {error}")]
ReturnCoerce {
name: String,
error: Box<CoerceError>,
},
#[error("Couldn't coerce value for field `{field_name}` of `{record}`: {error}")]
FieldCoerce {
record: String,
field_name: String,
error: Box<CoerceError>,
},
#[error(
"Found changed value for field `{field}`, with record `{record}`, but field is readonly",
field = field.to_sql()
)]
FieldReadonly {
record: String,
field: Idiom,
},
#[error("Found field '{field}', but no such field exists for table '{table}'", field = field.to_sql())]
FieldUndefined {
table: String,
field: Idiom,
},
#[error("Found {value} for the Record ID but this is not a valid id")]
IdInvalid {
value: String,
},
#[error("Found {value} for the `id` field, but a specific record has been specified")]
IdMismatch {
value: String,
},
#[error("Found {value} for the `in` field, which does not match the existing field value")]
InOverride {
value: String,
},
#[error("Found {value} for the `out` field, which does not match the existing field value")]
OutOverride {
value: String,
},
#[error("{0}")]
Coerce(#[from] CoerceError),
#[error("{0}")]
Cast(#[from] CastError),
#[error("Cannot perform addition with '{0}' and '{1}'")]
TryAdd(String, String),
#[error("Cannot perform subtraction with '{0}' and '{1}'")]
TrySub(String, String),
#[error("Cannot perform multiplication with '{0}' and '{1}'")]
TryMul(String, String),
#[error("Cannot perform division with '{0}' and '{1}'")]
TryDiv(String, String),
#[error("Cannot perform remainder with '{0}' and '{1}'")]
TryRem(String, String),
#[error("Cannot raise the value '{0}' with '{1}'")]
TryPow(String, String),
#[error("Cannot negate the value '{0}'")]
TryNeg(String),
#[error("Cannot extend '{0}' as it is not an array")]
TryExtend(String),
#[error("Cannot convert from '{0}' to '{1}'")]
TryFrom(String, &'static str),
#[error("There was an error processing a remote HTTP request: {0}")]
#[cfg_attr(not(any(feature = "http", feature = "jwks")), expect(dead_code))]
Http(String),
#[error("There was an error processing a value in parallel: {0}")]
Channel(String),
#[error("I/O error: {0}")]
Io(#[from] IoError),
#[error("Tried to serialize a value which cannot be serialized.")]
Unencodable,
#[error("Key decoding error: {0}")]
Storekey(#[from] StorekeyError),
#[error("Versioned error: {0}")]
Revision(#[from] RevisionError),
#[error("Index is corrupted: {0}")]
CorruptedIndex(&'static str),
#[error("There was no suitable index supporting the expression: {exp}")]
NoIndexFoundForMatch {
exp: String,
},
#[error("A value can't be analyzed: {0}")]
AnalyzerError(String),
#[error("A value can't be highlighted: {0}")]
HighlightError(String),
#[error("FstError error: {0}")]
FstError(#[from] FstError),
#[error("Utf8 error: {0}")]
Utf8Error(#[from] FromUtf8Error),
#[error("Object Store error: {0}")]
ObsError(#[from] ObjectStoreError),
#[error("Duplicated Match reference: {mr}")]
DuplicatedMatchRef {
mr: MatchRef,
},
#[error("Timestamp arithmetic error: {0}")]
TimestampOverflow(String),
#[error("Internal database error: {0}")]
Internal(String),
#[error("Unimplemented functionality: {0}")]
Unimplemented(String),
#[error("Planner unsupported: {0}")]
PlannerUnsupported(String),
#[error("Planner not yet implemented: {0}")]
PlannerUnimplemented(String),
#[error("IAM error: {0}")]
IamError(#[from] IamError),
#[error("Scripting functions are not allowed")]
ScriptingNotAllowed,
#[error("Function '{0}' is not allowed to be executed")]
FunctionNotAllowed(String),
#[error("Access to network target '{0}' is not allowed")]
#[cfg_attr(not(feature = "http"), expect(dead_code))]
NetTargetNotAllowed(String),
#[error("There was an error creating the token")]
TokenMakingFailed,
#[error("No record was returned")]
NoRecordFound,
#[error("Username or Password was not provided")]
MissingUserOrPass,
#[error("No signin target to either SC or DB or NS or KV")]
NoSigninTarget,
#[error("The password did not verify")]
InvalidPass,
#[error("There was a problem with authentication")]
InvalidAuth,
#[error("There was an unexpected error while performing authentication")]
UnexpectedAuth,
#[error("There was a problem with signing up")]
InvalidSignup,
#[error("The node '{id}' already exists")]
ClAlreadyExists {
id: String,
},
#[error("The api '{value}' already exists")]
ApAlreadyExists {
value: String,
},
#[error("The method '{method}' is defined in more than one FOR clause on api '{value}'")]
ApMethodDuplicate {
value: String,
method: String,
},
#[error("The analyzer '{name}' already exists")]
AzAlreadyExists {
name: String,
},
#[error("The bucket '{value}' already exists")]
BuAlreadyExists {
value: String,
},
#[error("The database '{name}' already exists")]
DbAlreadyExists {
name: String,
},
#[error("The event '{name}' already exists")]
EvAlreadyExists {
name: String,
},
#[error("The field '{name}' already exists")]
FdAlreadyExists {
name: String,
},
#[error("The function '{name}' already exists")]
FcAlreadyExists {
name: String,
},
#[error("The module '{name}' already exists")]
MdAlreadyExists {
name: String,
},
#[error("The index '{name}' already exists")]
IxAlreadyExists {
name: String,
},
#[error("The model '{name}' already exists")]
MlAlreadyExists {
name: String,
},
#[error("The namespace '{name}' already exists")]
NsAlreadyExists {
name: String,
},
#[error("The param '${name}' already exists")]
PaAlreadyExists {
name: String,
},
#[error("The config for {name} already exists")]
CgAlreadyExists {
name: String,
},
#[error("The sequence '{name}' already exists")]
SeqAlreadyExists {
name: String,
},
#[error("The table '{name}' already exists")]
TbAlreadyExists {
name: String,
},
#[error("The namespace token '{name}' already exists")]
#[allow(dead_code)]
NtAlreadyExists {
name: String,
},
#[error("The database token '{name}' already exists")]
#[allow(dead_code)]
DtAlreadyExists {
name: String,
},
#[error("The root user '{name}' already exists")]
UserRootAlreadyExists {
name: String,
},
#[error("The user '{name}' already exists in the namespace '{ns}'")]
UserNsAlreadyExists {
name: String,
ns: String,
},
#[error("The user '{name}' already exists in the database '{db}'")]
UserDbAlreadyExists {
name: String,
ns: String,
db: String,
},
#[error("Database index `{name}` is currently building")]
IndexAlreadyBuilding {
name: String,
},
#[error("Index building has been cancelled: {reason}")]
IndexingBuildingCancelled {
reason: String,
},
#[error("The token has expired")]
ExpiredToken,
#[error("The session has expired")]
ExpiredSession,
#[error("Serialization error: {0}")]
Serialization(String),
#[error("The root access method '{ac}' already exists")]
AccessRootAlreadyExists {
ac: String,
},
#[error("The access method '{ac}' already exists in the namespace '{ns}'")]
AccessNsAlreadyExists {
ac: String,
ns: String,
},
#[error("The access method '{ac}' already exists in the database '{db}'")]
AccessDbAlreadyExists {
ac: String,
ns: String,
db: String,
},
#[error("The root access method '{ac}' does not exist")]
AccessRootNotFound {
ac: String,
},
#[error("The root access grant '{gr}' does not exist for '{ac}'")]
AccessGrantRootNotFound {
ac: String,
gr: String,
},
#[error("The access method '{ac}' does not exist in the namespace '{ns}'")]
AccessNsNotFound {
ac: String,
ns: String,
},
#[error("The access grant '{gr}' does not exist for '{ac}' in the namespace '{ns}'")]
AccessGrantNsNotFound {
ac: String,
gr: String,
ns: String,
},
#[error("The access method '{ac}' does not exist in the database '{db}'")]
AccessDbNotFound {
ac: String,
ns: String,
db: String,
},
#[error("The access grant '{gr}' does not exist for '{ac}' in the database '{db}'")]
AccessGrantDbNotFound {
ac: String,
gr: String,
ns: String,
db: String,
},
#[error("The access method cannot be defined on the requested level")]
AccessLevelMismatch,
#[error("The access method cannot be used in the requested operation")]
AccessMethodMismatch,
#[error("The access method does not exist")]
AccessNotFound,
#[error(
"The ES512 algorithm is not currently supported. Please use ES384 or another supported algorithm"
)]
AccessUnsupportedAlgorithm,
#[error("This access method has an invalid duration")]
AccessInvalidDuration,
#[error("This access method results in an invalid expiration")]
AccessInvalidExpiration,
#[error(
"Tokens issued by record access methods can be consumed by third parties and must have an expiration; DURATION FOR TOKEN cannot be NONE on TYPE RECORD access"
)]
AccessRecordTokenDurationRequired,
#[error("The record access signup query failed")]
AccessRecordSignupQueryFailed,
#[error("The record access signin query failed")]
AccessRecordSigninQueryFailed,
#[error("This record access method does not allow signup")]
AccessRecordNoSignup,
#[error("This record access method does not allow signin")]
AccessRecordNoSignin,
#[error("This bearer access method requires a key to be provided")]
AccessBearerMissingKey,
#[error("This bearer access grant has an invalid format")]
AccessGrantBearerInvalid,
#[error("This access grant has an invalid subject")]
AccessGrantInvalidSubject,
#[error("This access grant has been revoked")]
AccessGrantRevoked,
#[error("Found {value} for the Record ID but this is not a valid table name")]
TbInvalid {
value: String,
},
#[error("{variant} destructuring method is not supported here")]
UnsupportedDestructure {
variant: String,
},
#[doc(hidden)]
#[error("The underlying datastore does not support versioned queries")]
UnsupportedVersionedQueries,
#[error("There was an invalid storage version stored in the database")]
InvalidStorageVersion,
#[error(
"The data stored on disk is out-of-date with this version (Expected: {expected}, Actual: {actual}). \
Please follow the upgrade guides in the documentation, \
or use a clean storage directory if this is intended to be a new instance"
)]
OutdatedStorageVersion {
expected: u16,
actual: u16,
},
#[error("Size of query script exceeded maximum supported size of 4,294,967,295 bytes.")]
QueryTooLarge,
#[error("Failed to compute: \"{0}\", as the operation results in an arithmetic overflow.")]
ArithmeticOverflow(String),
#[error("Failed to compute: \"{0}\", as the operation results in a negative value.")]
ArithmeticNegativeOverflow(String),
#[error("Error while ordering a result: {0}.")]
#[cfg_attr(target_family = "wasm", expect(dead_code))]
OrderingError(String),
#[error("Found {found} for bound but expected {expected}.")]
InvalidBound {
found: String,
expected: String,
},
#[error("Exceeded the idiom recursion limit of {limit}.")]
IdiomRecursionLimitExceeded {
limit: u32,
},
#[error("Tried to use a `@` repeat recurse symbol in a position where it is not supported")]
UnsupportedRepeatRecurse,
#[error("Cannot construct a recursion plan when an instruction is provided")]
RecursionInstructionPlanConflict,
#[error("Expected a record ID during recursive graph traversal, but found `{value}`")]
InvalidRecursionTarget {
value: String,
},
#[error("Cannot delete `{0}` as it is referenced by `{1}` with an ON DELETE REJECT clause")]
DeleteRejectedByReference(String, String),
#[error(
"Cannot use the `REFERENCE` keyword with `TYPE {0}`. Specify only a `record` type, or a type containing only records, instead."
)]
ReferenceTypeConflict(String),
#[error(
"Cannot use the `REFERENCE` keyword on nested field `{0}`. Specify a referencing field at the root level instead."
)]
ReferenceNestedField(String),
#[error("An error occurred while updating references for `{0}`: {1}")]
RefsUpdateFailure(String, String),
#[error(
"Cannot set field `{name}` with type `{kind}` as it mismatched with field `{existing_name}` with type `{existing_kind}`"
)]
MismatchedFieldTypes {
name: String,
kind: String,
existing_name: String,
existing_kind: String,
},
#[error("An API error occurred: {0}")]
ApiError(ApiError),
#[error("The string could not be parsed into a bytesize")]
InvalidBytesize,
#[error("The string could not be parsed into a path: {0}")]
InvalidPath(String),
#[error("File access denied: {0}")]
FileAccessDenied(String),
#[error("No global bucket has been configured")]
NoGlobalBucket,
#[error("Bucket `{0}` is unavailable")]
BucketUnavailable(String),
#[error("Bucket is unavailable")]
GlobalBucketEnforced,
#[error("Bucket url could not be processed: {0}")]
#[cfg_attr(target_family = "wasm", expect(dead_code))]
InvalidBucketUrl(String),
#[error("Bucket backend is not supported")]
UnsupportedBackend,
#[error("Write operation is not supported, as bucket `{0}` is in read-only mode")]
ReadonlyBucket(String),
#[error("Operation for bucket `{0}` failed: {1}")]
ObjectStoreFailure(String, String),
#[error("Cannot use the `{0}` keyword with `COMPUTED`.")]
ComputedKeywordConflict(String),
#[error("Cannot define field `{0}` as `COMPUTED` since a nested field `{1}` already exists.")]
ComputedNestedFieldConflict(String, String),
#[error("Cannot define nested field `{0}` as parent field `{1}` is a `COMPUTED` field.")]
ComputedParentFieldConflict(String, String),
#[error("Cannot define field `{0}` as `COMPUTED` fields must be top-level.")]
ComputedNestedField(String),
#[error("Cyclic dependency detected among computed fields: {0}")]
ComputedFieldCycle(String),
#[error("Cannot use the `{0}` keyword on the `id` field.")]
IdFieldKeywordConflict(String),
#[error("Cannot use the `{0}` type on the `id` field, as that's not a valid record id key.")]
IdFieldUnsupportedKind(String),
#[error(
"Cannot generate a record id of type `{kind}` for the `{table}` table; specify an explicit record id, or declare the `id` field as `uuid` or `string` to auto-generate one."
)]
IdFieldGenerateUnsupported {
table: String,
kind: String,
},
#[error(
"Error with the event {0}. The ID of the namespace `{1}` does not match the namespace this event has been generated from."
)]
EvNamespaceMismatch(String, String),
#[error(
"Error with the event {0}. The ID of the database `{1}` does not match the database this event has been generated from."
)]
EvDatabaseMismatch(String, String),
#[error("The event {0} reached the max async event nesting depth: {1}.")]
EvReachMaxDepth(String, u16),
#[error("Computed fields cannot be indexed. Index: '{index}' - Field: '{field}'")]
ComputedFieldCannotBeIndexed {
field: String,
index: String,
},
}
impl Error {
#[cold]
#[track_caller]
pub fn unreachable<T: fmt::Display>(message: T) -> Error {
let location = std::panic::Location::caller();
let message = format!("{}:{}: {}", location.file(), location.line(), message);
Error::Unreachable(message)
}
pub fn is_schema_related(&self) -> bool {
matches!(
self,
Error::FieldCoerce { .. }
| Error::FieldValue { .. }
| Error::FieldReadonly { .. }
| Error::FieldUndefined { .. }
)
}
pub fn is_ignorable(&self) -> bool {
matches!(
self,
Error::Coerce(_)
| Error::Cast(_)
| Error::InvalidFunctionArguments { .. }
| Error::TryAdd(..)
| Error::TrySub(..)
| Error::TryMul(..)
| Error::TryDiv(..)
| Error::TryPow(..)
| Error::TryNeg(..)
| Error::TryExtend(..)
)
}
}
impl From<Error> for String {
fn from(e: Error) -> String {
e.to_string()
}
}
impl From<ApiError> for Error {
fn from(value: ApiError) -> Self {
Error::ApiError(value)
}
}
impl From<Base64Error> for Error {
fn from(_: Base64Error) -> Error {
Error::InvalidAuth
}
}
impl From<JWTError> for Error {
fn from(_: JWTError) -> Error {
Error::InvalidAuth
}
}
impl From<regex::Error> for Error {
fn from(error: regex::Error) -> Self {
Error::InvalidRegex(error.to_string())
}
}
impl From<InvalidHeaderName> for Error {
fn from(error: InvalidHeaderName) -> Self {
Error::Unreachable(error.to_string())
}
}
impl From<InvalidHeaderValue> for Error {
fn from(error: InvalidHeaderValue) -> Self {
Error::Unreachable(error.to_string())
}
}
impl From<ToStrError> for Error {
fn from(error: ToStrError) -> Self {
Error::Unreachable(error.to_string())
}
}
impl From<async_channel::RecvError> for Error {
fn from(e: async_channel::RecvError) -> Error {
Error::Channel(e.to_string())
}
}
impl<T> From<async_channel::SendError<T>> for Error {
fn from(e: async_channel::SendError<T>) -> Error {
Error::Channel(e.to_string())
}
}
#[cfg(any(feature = "http", feature = "jwks"))]
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Error {
Error::Http(e.to_string())
}
}
#[cfg(storage)]
impl<S, D, I> From<SortError<S, D, I>> for Error
where
S: std::error::Error,
D: std::error::Error,
I: std::error::Error,
{
fn from(e: SortError<S, D, I>) -> Error {
Error::Internal(e.to_string())
}
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}
impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
Self::Serialization(msg.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_anyhow_to_types_error_signup_query_failed() {
let error = anyhow::Error::new(Error::AccessRecordSignupQueryFailed);
let types_error = anyhow_to_types_error(error);
assert!(
types_error.is_query(),
"expected Query error, got {} with message: {}",
types_error.kind_str(),
types_error.message()
);
}
#[test]
fn test_anyhow_to_types_error_signin_query_failed() {
let error = anyhow::Error::new(Error::AccessRecordSigninQueryFailed);
let types_error = anyhow_to_types_error(error);
assert!(
types_error.is_query(),
"expected Query error, got {} with message: {}",
types_error.kind_str(),
types_error.message()
);
}
}