1use 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>;