1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum EntityHydrationError {
8 #[error("EntityHydrationError - UninitializedFieldError: {0}")]
9 UninitializedFieldError(#[from] derive_builder::UninitializedFieldError),
10 #[error("EntityHydrationError - Deserialization: {0}")]
11 EventDeserialization(#[from] serde_json::Error),
12}
13
14#[derive(Error, Debug)]
15#[error("CursorDestructureError: couldn't turn {0} into {1}")]
16pub struct CursorDestructureError(&'static str, &'static str);
17
18impl From<(&'static str, &'static str)> for CursorDestructureError {
19 fn from((name, variant): (&'static str, &'static str)) -> Self {
20 Self(name, variant)
21 }
22}
23
24#[doc(hidden)]
25pub fn parse_constraint_detail_value(detail: Option<&str>) -> Option<String> {
37 let detail = detail?;
38 let start = detail.find("=(")? + 2;
39 let end = detail.rfind(") already")?;
40 if start <= end {
41 Some(detail[start..end].to_string())
42 } else {
43 None
44 }
45}
46
47#[doc(hidden)]
48pub fn extract_constraint_value(db_err: &dyn sqlx::error::DatabaseError) -> Option<String> {
56 let pg_err = db_err.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()?;
57 parse_constraint_detail_value(pg_err.detail())
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum ConstraintKind {
65 Unique,
66 ForeignKey,
67 Check,
68}
69
70#[doc(hidden)]
71pub fn is_classified_constraint_violation(db_err: &dyn sqlx::error::DatabaseError) -> bool {
76 db_err.is_unique_violation() || db_err.is_foreign_key_violation() || db_err.is_check_violation()
77}
78
79#[doc(hidden)]
80pub struct NotFoundValue<'a, T: ?Sized>(pub &'a T);
83
84impl<T: std::fmt::Display + ?Sized> NotFoundValue<'_, T> {
85 pub fn to_not_found_value(&self) -> String {
86 self.0.to_string()
87 }
88}
89
90#[doc(hidden)]
91pub trait ToNotFoundValueFallback {
92 fn to_not_found_value(&self) -> String;
93}
94
95impl<T: std::fmt::Debug + ?Sized> ToNotFoundValueFallback for NotFoundValue<'_, T> {
96 fn to_not_found_value(&self) -> String {
97 format!("{:?}", self.0)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn parse_simple_uuid_value() {
107 let detail = Some("Key (id)=(550e8400-e29b-41d4-a716-446655440000) already exists.");
108 assert_eq!(
109 parse_constraint_detail_value(detail),
110 Some("550e8400-e29b-41d4-a716-446655440000".to_string())
111 );
112 }
113
114 #[test]
115 fn parse_string_value() {
116 let detail = Some("Key (email)=(user@example.com) already exists.");
117 assert_eq!(
118 parse_constraint_detail_value(detail),
119 Some("user@example.com".to_string())
120 );
121 }
122
123 #[test]
124 fn parse_composite_key_value() {
125 let detail = Some("Key (tenant_id, email)=(abc, user@example.com) already exists.");
126 assert_eq!(
127 parse_constraint_detail_value(detail),
128 Some("abc, user@example.com".to_string())
129 );
130 }
131
132 #[test]
133 fn parse_none_detail() {
134 assert_eq!(parse_constraint_detail_value(None), None);
135 }
136
137 #[test]
138 fn parse_unexpected_format() {
139 let detail = Some("something unexpected");
140 assert_eq!(parse_constraint_detail_value(detail), None);
141 }
142
143 #[test]
144 fn parse_value_containing_parentheses() {
145 let detail = Some("Key (name)=(foo (bar)) already exists.");
146 assert_eq!(
147 parse_constraint_detail_value(detail),
148 Some("foo (bar)".to_string())
149 );
150 }
151
152 #[test]
153 fn parse_empty_value() {
154 let detail = Some("Key (col)=() already exists.");
155 assert_eq!(parse_constraint_detail_value(detail), Some("".to_string()));
156 }
157
158 #[test]
159 fn not_found_value_uses_display_when_available() {
160 #[allow(unused_imports)]
161 use crate::ToNotFoundValueFallback;
162
163 let val = "hello";
165 assert_eq!(NotFoundValue(val).to_not_found_value(), "hello");
166
167 let num = 42;
169 assert_eq!(NotFoundValue(&num).to_not_found_value(), "42");
170 }
171
172 #[test]
173 fn not_found_value_falls_back_to_debug() {
174 use crate::ToNotFoundValueFallback;
175
176 #[derive(Debug)]
178 #[allow(dead_code)]
179 struct OnlyDebug(i32);
180
181 let val = OnlyDebug(7);
182 assert_eq!(NotFoundValue(&val).to_not_found_value(), "OnlyDebug(7)");
183 }
184}