use thiserror::Error;
#[derive(Error, Debug)]
pub enum EntityHydrationError {
#[error("EntityHydrationError - UninitializedFieldError: {0}")]
UninitializedFieldError(#[from] derive_builder::UninitializedFieldError),
#[error("EntityHydrationError - Deserialization: {0}")]
EventDeserialization(#[from] serde_json::Error),
}
#[derive(Error, Debug)]
#[error("CursorDestructureError: couldn't turn {0} into {1}")]
pub struct CursorDestructureError(&'static str, &'static str);
impl From<(&'static str, &'static str)> for CursorDestructureError {
fn from((name, variant): (&'static str, &'static str)) -> Self {
Self(name, variant)
}
}
#[doc(hidden)]
pub fn parse_constraint_detail_value(detail: Option<&str>) -> Option<String> {
let detail = detail?;
let start = detail.find("=(")? + 2;
let end = detail.rfind(") already")?;
if start <= end {
Some(detail[start..end].to_string())
} else {
None
}
}
#[doc(hidden)]
pub fn extract_constraint_value(db_err: &dyn sqlx::error::DatabaseError) -> Option<String> {
let pg_err = db_err.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()?;
parse_constraint_detail_value(pg_err.detail())
}
#[doc(hidden)]
pub struct NotFoundValue<'a, T: ?Sized>(pub &'a T);
impl<T: std::fmt::Display + ?Sized> NotFoundValue<'_, T> {
pub fn to_not_found_value(&self) -> String {
self.0.to_string()
}
}
#[doc(hidden)]
pub trait ToNotFoundValueFallback {
fn to_not_found_value(&self) -> String;
}
impl<T: std::fmt::Debug + ?Sized> ToNotFoundValueFallback for NotFoundValue<'_, T> {
fn to_not_found_value(&self) -> String {
format!("{:?}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_simple_uuid_value() {
let detail = Some("Key (id)=(550e8400-e29b-41d4-a716-446655440000) already exists.");
assert_eq!(
parse_constraint_detail_value(detail),
Some("550e8400-e29b-41d4-a716-446655440000".to_string())
);
}
#[test]
fn parse_string_value() {
let detail = Some("Key (email)=(user@example.com) already exists.");
assert_eq!(
parse_constraint_detail_value(detail),
Some("user@example.com".to_string())
);
}
#[test]
fn parse_composite_key_value() {
let detail = Some("Key (tenant_id, email)=(abc, user@example.com) already exists.");
assert_eq!(
parse_constraint_detail_value(detail),
Some("abc, user@example.com".to_string())
);
}
#[test]
fn parse_none_detail() {
assert_eq!(parse_constraint_detail_value(None), None);
}
#[test]
fn parse_unexpected_format() {
let detail = Some("something unexpected");
assert_eq!(parse_constraint_detail_value(detail), None);
}
#[test]
fn parse_value_containing_parentheses() {
let detail = Some("Key (name)=(foo (bar)) already exists.");
assert_eq!(
parse_constraint_detail_value(detail),
Some("foo (bar)".to_string())
);
}
#[test]
fn parse_empty_value() {
let detail = Some("Key (col)=() already exists.");
assert_eq!(parse_constraint_detail_value(detail), Some("".to_string()));
}
#[test]
fn not_found_value_uses_display_when_available() {
#[allow(unused_imports)]
use crate::ToNotFoundValueFallback;
let val = "hello";
assert_eq!(NotFoundValue(val).to_not_found_value(), "hello");
let num = 42;
assert_eq!(NotFoundValue(&num).to_not_found_value(), "42");
}
#[test]
fn not_found_value_falls_back_to_debug() {
use crate::ToNotFoundValueFallback;
#[derive(Debug)]
#[allow(dead_code)]
struct OnlyDebug(i32);
let val = OnlyDebug(7);
assert_eq!(NotFoundValue(&val).to_not_found_value(), "OnlyDebug(7)");
}
}