1pub use crate::models::{
2 BaseModel, ByUuid, CreatableModel, CreateListenerList, DeletableModel, DeleteHandlerList,
3 DuplicableModel, DuplicateHandlerList, EventEmittingModel, Fetchable, IntoAdminApiObject,
4 IntoApiObject, ListenerPriority, ModelHandlerList, OrderedJson, UpdatableModel,
5 UpdateHandlerList,
6};
7use futures_util::{StreamExt, TryStreamExt};
8pub use schema_extension_core::finish_extendible;
9use std::borrow::Cow;
10
11pub trait IteratorExt<R, E>: Iterator<Item = Result<R, E>> {
12 fn try_collect_vec(self) -> Result<Vec<R>, E>
13 where
14 Self: Sized,
15 {
16 let mut vec = Vec::new();
17
18 let (hint_min, hint_max) = self.size_hint();
19 if let Some(hint_max) = hint_max
20 && hint_min == hint_max
21 {
22 vec.reserve_exact(hint_max);
23 }
24
25 for item in self {
26 vec.push(item?);
27 }
28
29 Ok(vec)
30 }
31
32 fn try_collect_vecdeque(self) -> Result<std::collections::VecDeque<R>, E>
33 where
34 Self: Sized,
35 {
36 let mut deque = std::collections::VecDeque::new();
37
38 let (hint_min, hint_max) = self.size_hint();
39 if let Some(hint_max) = hint_max
40 && hint_min == hint_max
41 {
42 deque.reserve_exact(hint_max);
43 }
44
45 for item in self {
46 deque.push_back(item?);
47 }
48
49 Ok(deque)
50 }
51
52 fn try_collect_set(self) -> Result<std::collections::HashSet<R>, E>
53 where
54 Self: Sized,
55 R: std::hash::Hash + Eq,
56 {
57 let mut set = std::collections::HashSet::new();
58
59 for item in self {
60 set.insert(item?);
61 }
62
63 Ok(set)
64 }
65}
66
67impl<R, E, T: Iterator<Item = Result<R, E>>> IteratorExt<R, E> for T {}
68
69#[async_trait::async_trait]
70pub trait AsyncIteratorExt<R: Send, E: Send, F: Future<Output = Result<R, E>> + Send>:
71 Iterator<Item = F> + Sized + Send
72{
73 async fn try_collect_async_vec(self) -> Result<Vec<R>, E>
74 where
75 Self: Sized,
76 {
77 let mut vec = Vec::new();
78
79 let (hint_min, hint_max) = self.size_hint();
80 if let Some(hint_max) = hint_max
81 && hint_min == hint_max
82 {
83 vec.reserve_exact(hint_max);
84 }
85
86 let mut result_stream = futures_util::stream::iter(self).buffered(25);
87
88 while let Some(result) = result_stream.try_next().await? {
89 vec.push(result);
90 }
91
92 Ok(vec)
93 }
94}
95
96impl<
97 R: Send,
98 E: Send,
99 F: Future<Output = Result<R, E>> + Send,
100 T: Iterator<Item = F> + Sized + Send,
101> AsyncIteratorExt<R, E, F> for T
102{
103}
104
105pub trait OptionExt<T> {
106 fn try_map<R, E, F: FnMut(T) -> Result<R, E>>(self, f: F) -> Result<Option<R>, E>;
107}
108
109impl<T> OptionExt<T> for Option<T> {
110 #[inline]
111 fn try_map<R, E, F: FnMut(T) -> Result<R, E>>(self, mut f: F) -> Result<Option<R>, E> {
112 match self {
113 Some(item) => Ok(Some(f(item)?)),
114 None => Ok(None),
115 }
116 }
117}
118
119#[async_trait::async_trait]
120pub trait AsyncOptionExt<T, Fut: Future<Output = T>> {
121 async fn awaited(self) -> Option<T>;
122}
123
124#[async_trait::async_trait]
125impl<T, Fut: Future<Output = T> + Send> AsyncOptionExt<T, Fut> for Option<Fut> {
126 #[inline]
127 async fn awaited(self) -> Option<T> {
128 match self {
129 Some(item) => Some(item.await),
130 None => None,
131 }
132 }
133}
134
135pub trait SqlxErrorExt {
136 fn is_unique_violation(&self) -> bool;
137 fn is_foreign_key_violation(&self) -> bool;
138 fn is_check_violation(&self) -> bool;
139 fn is_not_null_violation(&self) -> bool;
140
141 fn code(&self) -> Option<Cow<'_, str>>;
142 fn message(&self) -> Option<&str>;
143}
144
145impl SqlxErrorExt for sqlx::Error {
146 #[inline]
147 fn is_unique_violation(&self) -> bool {
148 self.as_database_error()
149 .is_some_and(|e| e.is_unique_violation())
150 }
151
152 #[inline]
153 fn is_foreign_key_violation(&self) -> bool {
154 self.as_database_error()
155 .is_some_and(|e| e.is_foreign_key_violation())
156 }
157
158 #[inline]
159 fn is_check_violation(&self) -> bool {
160 self.as_database_error()
161 .is_some_and(|e| e.is_check_violation())
162 }
163
164 #[inline]
165 fn is_not_null_violation(&self) -> bool {
166 self.code().as_deref() == Some("23502")
167 }
168
169 #[inline]
170 fn code(&self) -> Option<Cow<'_, str>> {
171 self.as_database_error().and_then(|e| e.code())
172 }
173
174 #[inline]
175 fn message(&self) -> Option<&str> {
176 self.as_database_error().map(|e| e.message())
177 }
178}
179
180pub trait StringExt: Sized {
181 fn optional(&self) -> Option<&Self>;
183
184 fn into_optional(self) -> Option<Self>;
186}
187
188impl StringExt for String {
189 #[inline]
190 fn optional(&self) -> Option<&Self> {
191 if self.is_empty() { None } else { Some(self) }
192 }
193
194 #[inline]
195 fn into_optional(self) -> Option<Self> {
196 if self.is_empty() { None } else { Some(self) }
197 }
198}
199
200impl StringExt for compact_str::CompactString {
201 #[inline]
202 fn optional(&self) -> Option<&Self> {
203 if self.is_empty() { None } else { Some(self) }
204 }
205
206 #[inline]
207 fn into_optional(self) -> Option<Self> {
208 if self.is_empty() { None } else { Some(self) }
209 }
210}
211
212impl StringExt for &str {
213 #[inline]
214 fn optional(&self) -> Option<&Self> {
215 if self.is_empty() { None } else { Some(self) }
216 }
217
218 #[inline]
219 fn into_optional(self) -> Option<Self> {
220 if self.is_empty() { None } else { Some(self) }
221 }
222}