Skip to main content

shared/
prelude.rs

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
140    fn code(&self) -> Option<Cow<'_, str>>;
141    fn message(&self) -> Option<&str>;
142}
143
144impl SqlxErrorExt for sqlx::Error {
145    #[inline]
146    fn is_unique_violation(&self) -> bool {
147        self.as_database_error()
148            .is_some_and(|e| e.is_unique_violation())
149    }
150
151    #[inline]
152    fn is_foreign_key_violation(&self) -> bool {
153        self.as_database_error()
154            .is_some_and(|e| e.is_foreign_key_violation())
155    }
156
157    #[inline]
158    fn is_check_violation(&self) -> bool {
159        self.as_database_error()
160            .is_some_and(|e| e.is_check_violation())
161    }
162
163    #[inline]
164    fn code(&self) -> Option<Cow<'_, str>> {
165        self.as_database_error().and_then(|e| e.code())
166    }
167
168    #[inline]
169    fn message(&self) -> Option<&str> {
170        self.as_database_error().map(|e| e.message())
171    }
172}
173
174pub trait StringExt: Sized {
175    /// Returns Some if the string has content, otherwise None.
176    fn optional(&self) -> Option<&Self>;
177
178    /// Returns Some if the string has content, otherwise None.
179    fn into_optional(self) -> Option<Self>;
180}
181
182impl StringExt for String {
183    #[inline]
184    fn optional(&self) -> Option<&Self> {
185        if self.is_empty() { None } else { Some(self) }
186    }
187
188    #[inline]
189    fn into_optional(self) -> Option<Self> {
190        if self.is_empty() { None } else { Some(self) }
191    }
192}
193
194impl StringExt for compact_str::CompactString {
195    #[inline]
196    fn optional(&self) -> Option<&Self> {
197        if self.is_empty() { None } else { Some(self) }
198    }
199
200    #[inline]
201    fn into_optional(self) -> Option<Self> {
202        if self.is_empty() { None } else { Some(self) }
203    }
204}
205
206impl StringExt for &str {
207    #[inline]
208    fn optional(&self) -> Option<&Self> {
209        if self.is_empty() { None } else { Some(self) }
210    }
211
212    #[inline]
213    fn into_optional(self) -> Option<Self> {
214        if self.is_empty() { None } else { Some(self) }
215    }
216}