#![deny(clippy::wildcard_enum_match_arm)]
use common::{LeafError, internal_todo};
use surrealdb_types::{AlreadyExistsError, Error as TypesError, NotFoundError, ToSql};
use crate::expr::Idiom;
use crate::val::{CoerceError, RecordId};
#[derive(Debug, thiserror::Error)]
pub(crate) enum Error {
#[error("Database record `{record}` already exists", record = record.to_sql())]
RecordExists {
record: RecordId,
},
#[error("The record '{rid}' does not exist")]
IdNotFound {
rid: String,
},
#[error("Found {value} for the `id` field, but a specific record has been specified")]
IdMismatch {
value: 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("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("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(
"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(
"Cannot write to table '{table}': the DEFAULT, VALUE, ASSERT or COMPUTED clauses of the fields {fields} depend on each other in a cycle"
)]
FieldDependencyCycle {
table: String,
fields: String,
},
#[error("Couldn't coerce value for field `{field_name}` of `{record}`: {error}")]
FieldCoerce {
record: String,
field_name: String,
error: Box<CoerceError>,
},
#[error(
"Cannot read `{0}` in a JSON Patch operation: the field's PERMISSIONS FOR select clause does not allow it"
)]
PatchPointerNotAllowed(String),
#[error("Cannot delete `{0}` as it is referenced by `{1}` with an ON DELETE REJECT clause")]
DeleteRejectedByReference(String, String),
#[error("An error occurred while updating references for `{0}`: {1}")]
RefsUpdateFailure(String, 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),
}
impl Error {
pub(crate) fn is_schema_related(&self) -> bool {
matches!(
self,
Error::FieldCoerce { .. }
| Error::FieldValue { .. }
| Error::FieldReadonly { .. }
| Error::FieldUndefined { .. }
)
}
}
impl LeafError for Error {
fn map_kind(self, message: String) -> TypesError {
match self {
Error::RecordExists {
record,
} => TypesError::already_exists(
message,
AlreadyExistsError::Record {
id: record.to_sql(),
},
),
Error::FieldDependencyCycle {
..
} => TypesError::configuration(message, None),
Error::IdNotFound {
rid,
} => TypesError::not_found(
message,
NotFoundError::Record {
id: rid,
},
),
Error::PatchPointerNotAllowed(..) => TypesError::not_allowed(message, None),
Error::IdMismatch {
..
}
| Error::IdFieldGenerateUnsupported {
..
}
| Error::InOverride {
..
}
| Error::OutOverride {
..
}
| Error::TableCheck {
..
}
| Error::TableIsView {
..
}
| Error::FieldValue {
..
}
| Error::FieldReadonly {
..
}
| Error::FieldUndefined {
..
}
| Error::FieldCoerce {
..
}
| Error::DeleteRejectedByReference(..)
| Error::RefsUpdateFailure(..)
| Error::EvNamespaceMismatch(..)
| Error::EvDatabaseMismatch(..)
| Error::EvReachMaxDepth(..) => internal_todo(message),
}
}
}