use thiserror::Error;
#[derive(Error, Debug)]
pub enum FoldDbCoreError {
#[error("Atom not found: {id}")]
AtomNotFound { id: String },
#[error("Molecule not found: {molecule_uuid}")]
MoleculeNotFound { molecule_uuid: String },
#[error("Molecule type mismatch for {molecule_uuid}: expected {expected}, found {actual}")]
MoleculeTypeMismatch {
molecule_uuid: String,
expected: String,
actual: String,
},
#[error("Ghost UUID detected: field {field_name} has molecule_uuid {uuid} but no corresponding Molecule exists")]
GhostUuidDetected { field_name: String, uuid: String },
#[error("Field not found: {field_name} in schema {schema_name}")]
FieldNotFound {
field_name: String,
schema_name: String,
},
#[error("Invalid field operation: {operation} on field {field_name} of type {field_type}")]
InvalidFieldOperation {
operation: String,
field_name: String,
field_type: String,
},
#[error("Field validation failed for {field_name}: {reason}")]
FieldValidationFailed { field_name: String, reason: String },
#[error("Schema not found: {schema_name}")]
SchemaNotFound { schema_name: String },
#[error("Schema validation failed for {schema_name}: {reason}")]
SchemaValidationFailed { schema_name: String, reason: String },
#[error("Range schema error for {schema_name}: {reason}")]
RangeSchemaError { schema_name: String, reason: String },
#[error("Database operation failed: {operation} - {reason}")]
DatabaseError { operation: String, reason: String },
#[error("Serialization error: {context} - {reason}")]
SerializationError { context: String, reason: String },
#[error("Deserialization error: {context} - {reason}")]
DeserializationError { context: String, reason: String },
#[error("Lock error: failed to acquire {lock_type} lock for {resource}")]
LockError { lock_type: String, resource: String },
#[error("Concurrency error: {operation} failed due to {reason}")]
ConcurrencyError { operation: String, reason: String },
#[error("Transform not found: {transform_name}")]
TransformNotFound { transform_name: String },
#[error("Transform execution failed: {transform_name} - {reason}")]
TransformExecutionFailed {
transform_name: String,
reason: String,
},
#[error("Permission denied: {operation} on {resource} for {subject}")]
PermissionDenied {
operation: String,
resource: String,
subject: String,
},
#[error("Operation failed: {operation} - {reason}")]
OperationFailed { operation: String, reason: String },
}
impl FoldDbCoreError {
pub fn atom_not_found(id: impl Into<String>) -> Self {
Self::AtomNotFound { id: id.into() }
}
pub fn molecule_not_found(molecule_uuid: impl Into<String>) -> Self {
Self::MoleculeNotFound {
molecule_uuid: molecule_uuid.into(),
}
}
pub fn molecule_type_mismatch(
molecule_uuid: impl Into<String>,
expected: impl Into<String>,
actual: impl Into<String>,
) -> Self {
Self::MoleculeTypeMismatch {
molecule_uuid: molecule_uuid.into(),
expected: expected.into(),
actual: actual.into(),
}
}
pub fn ghost_uuid_detected(field_name: impl Into<String>, uuid: impl Into<String>) -> Self {
Self::GhostUuidDetected {
field_name: field_name.into(),
uuid: uuid.into(),
}
}
pub fn field_not_found(field_name: impl Into<String>, schema_name: impl Into<String>) -> Self {
Self::FieldNotFound {
field_name: field_name.into(),
schema_name: schema_name.into(),
}
}
pub fn invalid_field_operation(
operation: impl Into<String>,
field_name: impl Into<String>,
field_type: impl Into<String>,
) -> Self {
Self::InvalidFieldOperation {
operation: operation.into(),
field_name: field_name.into(),
field_type: field_type.into(),
}
}
pub fn field_validation_failed(
field_name: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self::FieldValidationFailed {
field_name: field_name.into(),
reason: reason.into(),
}
}
pub fn schema_not_found(schema_name: impl Into<String>) -> Self {
Self::SchemaNotFound {
schema_name: schema_name.into(),
}
}
pub fn schema_validation_failed(
schema_name: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self::SchemaValidationFailed {
schema_name: schema_name.into(),
reason: reason.into(),
}
}
pub fn range_schema_error(schema_name: impl Into<String>, reason: impl Into<String>) -> Self {
Self::RangeSchemaError {
schema_name: schema_name.into(),
reason: reason.into(),
}
}
pub fn database_error(operation: impl Into<String>, reason: impl Into<String>) -> Self {
Self::DatabaseError {
operation: operation.into(),
reason: reason.into(),
}
}
pub fn serialization_error(context: impl Into<String>, reason: impl Into<String>) -> Self {
Self::SerializationError {
context: context.into(),
reason: reason.into(),
}
}
pub fn deserialization_error(context: impl Into<String>, reason: impl Into<String>) -> Self {
Self::DeserializationError {
context: context.into(),
reason: reason.into(),
}
}
pub fn lock_error(lock_type: impl Into<String>, resource: impl Into<String>) -> Self {
Self::LockError {
lock_type: lock_type.into(),
resource: resource.into(),
}
}
pub fn concurrency_error(operation: impl Into<String>, reason: impl Into<String>) -> Self {
Self::ConcurrencyError {
operation: operation.into(),
reason: reason.into(),
}
}
pub fn transform_not_found(transform_name: impl Into<String>) -> Self {
Self::TransformNotFound {
transform_name: transform_name.into(),
}
}
pub fn transform_execution_failed(
transform_name: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self::TransformExecutionFailed {
transform_name: transform_name.into(),
reason: reason.into(),
}
}
pub fn permission_denied(
operation: impl Into<String>,
resource: impl Into<String>,
subject: impl Into<String>,
) -> Self {
Self::PermissionDenied {
operation: operation.into(),
resource: resource.into(),
subject: subject.into(),
}
}
pub fn operation_failed(operation: impl Into<String>, reason: impl Into<String>) -> Self {
Self::OperationFailed {
operation: operation.into(),
reason: reason.into(),
}
}
}
impl From<sled::Error> for FoldDbCoreError {
fn from(error: sled::Error) -> Self {
Self::DatabaseError {
operation: "sled_operation".to_string(),
reason: error.to_string(),
}
}
}
impl From<serde_json::Error> for FoldDbCoreError {
fn from(error: serde_json::Error) -> Self {
if error.is_syntax() || error.is_data() {
Self::DeserializationError {
context: "json_deserialization".to_string(),
reason: error.to_string(),
}
} else {
Self::SerializationError {
context: "json_serialization".to_string(),
reason: error.to_string(),
}
}
}
}
impl From<crate::schema::types::SchemaError> for FoldDbCoreError {
fn from(error: crate::schema::types::SchemaError) -> Self {
use crate::schema::types::SchemaError;
match error {
SchemaError::NotFound(msg) => Self::SchemaNotFound { schema_name: msg },
SchemaError::InvalidField(msg) => Self::FieldValidationFailed {
field_name: "unknown".to_string(),
reason: msg,
},
SchemaError::InvalidPermission(msg) => Self::PermissionDenied {
operation: "schema_access".to_string(),
resource: "schema".to_string(),
subject: msg,
},
SchemaError::InvalidTransform(msg) => Self::TransformExecutionFailed {
transform_name: "unknown".to_string(),
reason: msg,
},
SchemaError::InvalidData(msg) => Self::SchemaValidationFailed {
schema_name: "unknown".to_string(),
reason: msg,
},
SchemaError::InvalidDSL(msg) => Self::TransformExecutionFailed {
transform_name: "dsl_transform".to_string(),
reason: msg,
},
SchemaError::MappingError(msg) => Self::OperationFailed {
operation: "schema_mapping".to_string(),
reason: msg,
},
}
}
}
impl From<Box<dyn std::error::Error>> for FoldDbCoreError {
fn from(error: Box<dyn std::error::Error>) -> Self {
Self::OperationFailed {
operation: "generic_operation".to_string(),
reason: error.to_string(),
}
}
}
pub type FoldDbCoreResult<T> = Result<T, FoldDbCoreError>;
impl FoldDbCoreError {
pub fn mutex_lock_timeout(resource: impl Into<String>) -> Self {
Self::lock_error("mutex", resource)
}
pub fn rwlock_timeout(resource: impl Into<String>) -> Self {
Self::lock_error("rwlock", resource)
}
pub fn field_type_validation_error(
field_name: impl Into<String>,
expected_type: impl Into<String>,
actual_type: impl Into<String>,
) -> Self {
Self::field_validation_failed(
field_name,
format!(
"Expected field type {}, found {}",
expected_type.into(),
actual_type.into()
),
)
}
pub fn range_key_validation_error(
schema_name: impl Into<String>,
field_name: impl Into<String>,
issue: impl Into<String>,
) -> Self {
Self::range_schema_error(
schema_name,
format!(
"Range key validation failed for field {}: {}",
field_name.into(),
issue.into()
),
)
}
pub fn molecule_creation_failed(
field_name: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self::invalid_field_operation(
"create_molecule".to_string(),
field_name,
format!("Molecule creation failed: {}", reason.into()),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation_helpers() {
let error = FoldDbCoreError::atom_not_found("test-uuid");
assert!(error.to_string().contains("test-uuid"));
let error = FoldDbCoreError::field_not_found("test_field", "test_schema");
assert!(error.to_string().contains("test_field"));
assert!(error.to_string().contains("test_schema"));
let error = FoldDbCoreError::mutex_lock_timeout("test_resource");
assert!(error.to_string().contains("mutex"));
assert!(error.to_string().contains("test_resource"));
}
#[test]
fn test_error_conversions() {
let sled_error = sled::Error::Unsupported("test error".to_string());
let core_error: FoldDbCoreError = sled_error.into();
match core_error {
FoldDbCoreError::DatabaseError { operation, reason } => {
assert_eq!(operation, "sled_operation");
assert!(reason.contains("test error"));
}
_ => panic!("Expected DatabaseError"),
}
let json_str = r#"{"invalid": json}"#;
let json_error = serde_json::from_str::<serde_json::Value>(json_str).unwrap_err();
let core_error: FoldDbCoreError = json_error.into();
match core_error {
FoldDbCoreError::DeserializationError { context, .. } => {
assert_eq!(context, "json_deserialization");
}
_ => panic!("Expected DeserializationError"),
}
}
}