Skip to main content

mqdb_core/
error.rs

1// Copyright 2025-2026 LabOverWire. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use thiserror::Error;
5
6#[derive(Error, Debug)]
7pub enum Error {
8    #[cfg(feature = "fjall-backend")]
9    #[error("storage error: {0}")]
10    Storage(#[from] fjall::Error),
11
12    #[error("storage error: {0}")]
13    StorageGeneric(String),
14
15    #[error("entity not found: {entity} id={id}")]
16    NotFound { entity: String, id: String },
17
18    #[error("serialization error: {0}")]
19    Serialization(#[from] serde_json::Error),
20
21    #[error("invalid key format: {0}")]
22    InvalidKey(String),
23
24    #[error("validation error: {0}")]
25    Validation(String),
26
27    #[error("constraint violation: {0}")]
28    ConstraintViolation(String),
29
30    #[error("internal error: {0}")]
31    Internal(String),
32
33    #[error("system time error: {0}")]
34    SystemTime(String),
35
36    #[error("data corruption detected: {entity}/{id} - checksum mismatch")]
37    Corruption { entity: String, id: String },
38
39    #[error("backup/restore failed: {0}")]
40    BackupFailed(String),
41
42    #[error("concurrent modification conflict: {0}")]
43    Conflict(String),
44
45    #[error("schema validation failed: {entity}.{field} - {reason}")]
46    SchemaViolation {
47        entity: String,
48        field: String,
49        reason: String,
50    },
51
52    #[error("unique constraint violation: {entity}.{field} value '{value}' already exists")]
53    UniqueViolation {
54        entity: String,
55        field: String,
56        value: String,
57    },
58
59    #[error(
60        "foreign key violation: {entity}.{field} references non-existent {target_entity}/{target_id}"
61    )]
62    ForeignKeyViolation {
63        entity: String,
64        field: String,
65        target_entity: String,
66        target_id: String,
67    },
68
69    #[error(
70        "foreign key restrict: cannot delete {entity}/{id} - referenced by {referencing_entity}"
71    )]
72    ForeignKeyRestrict {
73        entity: String,
74        id: String,
75        referencing_entity: String,
76    },
77
78    #[error("not null constraint violation: {entity}.{field} cannot be null")]
79    NotNullViolation { entity: String, field: String },
80
81    #[error("invalid foreign key value type")]
82    InvalidForeignKey,
83
84    #[error("forbidden: {0}")]
85    Forbidden(String),
86
87    #[error("cascade blocked: cannot delete {0} - cross-owned entity has non-nullable FK field")]
88    CascadeBlocked(Box<CascadeBlockedInfo>),
89}
90
91#[derive(Debug)]
92pub struct CascadeBlockedInfo {
93    pub entity: String,
94    pub id: String,
95    pub blocked_entity: String,
96    pub blocked_id: String,
97    pub blocked_field: String,
98    pub blocked_owner: String,
99}
100
101impl std::fmt::Display for CascadeBlockedInfo {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        write!(
104            f,
105            "{}/{} by {}/{} (field '{}', owner '{}')",
106            self.entity,
107            self.id,
108            self.blocked_entity,
109            self.blocked_id,
110            self.blocked_field,
111            self.blocked_owner
112        )
113    }
114}
115
116pub type Result<T> = std::result::Result<T, Error>;