1use std::borrow::Cow;
2
3pub use crate::models::{
4 BaseModel, ByUuid, CreatableModel, CreateListenerList, DeletableModel, DeleteListenerList,
5 EventEmittingModel, Fetchable, ListenerPriority, ModelHandlerList, UpdatableModel,
6 UpdateListenerList,
7};
8pub use schema_extension_core::finish_extendible;
9
10pub trait IteratorExt<R, E>: Iterator<Item = Result<R, E>> {
11 fn try_collect_vec(self) -> Result<Vec<R>, E>
12 where
13 Self: Sized,
14 {
15 let mut vec = Vec::new();
16
17 let (hint_min, hint_max) = self.size_hint();
18 if let Some(hint_max) = hint_max
19 && hint_min == hint_max
20 {
21 vec.reserve_exact(hint_max);
22 }
23
24 for item in self {
25 vec.push(item?);
26 }
27
28 Ok(vec)
29 }
30}
31
32impl<R, E, T: Iterator<Item = Result<R, E>>> IteratorExt<R, E> for T {}
33
34pub trait OptionExt<T> {
35 fn try_map<R, E, F: FnMut(T) -> Result<R, E>>(self, f: F) -> Result<Option<R>, E>;
36}
37
38impl<T> OptionExt<T> for Option<T> {
39 #[inline]
40 fn try_map<R, E, F: FnMut(T) -> Result<R, E>>(self, mut f: F) -> Result<Option<R>, E> {
41 match self {
42 Some(item) => Ok(Some(f(item)?)),
43 None => Ok(None),
44 }
45 }
46}
47
48#[async_trait::async_trait]
49pub trait AsyncOptionExt<T, Fut: Future<Output = T>> {
50 async fn awaited(self) -> Option<T>;
51}
52
53#[async_trait::async_trait]
54impl<T, Fut: Future<Output = T> + Send> AsyncOptionExt<T, Fut> for Option<Fut> {
55 #[inline]
56 async fn awaited(self) -> Option<T> {
57 match self {
58 Some(item) => Some(item.await),
59 None => None,
60 }
61 }
62}
63
64pub trait SqlxErrorExt {
65 fn is_unique_violation(&self) -> bool;
66 fn is_foreign_key_violation(&self) -> bool;
67 fn is_check_violation(&self) -> bool;
68
69 fn code(&self) -> Option<Cow<'_, str>>;
70 fn message(&self) -> Option<&str>;
71}
72
73impl SqlxErrorExt for sqlx::Error {
74 #[inline]
75 fn is_unique_violation(&self) -> bool {
76 self.as_database_error()
77 .is_some_and(|e| e.is_unique_violation())
78 }
79
80 #[inline]
81 fn is_foreign_key_violation(&self) -> bool {
82 self.as_database_error()
83 .is_some_and(|e| e.is_foreign_key_violation())
84 }
85
86 #[inline]
87 fn is_check_violation(&self) -> bool {
88 self.as_database_error()
89 .is_some_and(|e| e.is_check_violation())
90 }
91
92 #[inline]
93 fn code(&self) -> Option<Cow<'_, str>> {
94 self.as_database_error().and_then(|e| e.code())
95 }
96
97 #[inline]
98 fn message(&self) -> Option<&str> {
99 self.as_database_error().map(|e| e.message())
100 }
101}
102
103pub trait StringExt: Sized {
104 fn optional(&self) -> Option<&Self>;
106
107 fn into_optional(self) -> Option<Self>;
109}
110
111impl StringExt for String {
112 #[inline]
113 fn optional(&self) -> Option<&Self> {
114 if self.is_empty() { None } else { Some(self) }
115 }
116
117 #[inline]
118 fn into_optional(self) -> Option<Self> {
119 if self.is_empty() { None } else { Some(self) }
120 }
121}
122
123impl StringExt for compact_str::CompactString {
124 #[inline]
125 fn optional(&self) -> Option<&Self> {
126 if self.is_empty() { None } else { Some(self) }
127 }
128
129 #[inline]
130 fn into_optional(self) -> Option<Self> {
131 if self.is_empty() { None } else { Some(self) }
132 }
133}
134
135impl StringExt for &str {
136 #[inline]
137 fn optional(&self) -> Option<&Self> {
138 if self.is_empty() { None } else { Some(self) }
139 }
140
141 #[inline]
142 fn into_optional(self) -> Option<Self> {
143 if self.is_empty() { None } else { Some(self) }
144 }
145}