Skip to main content

shared/
prelude.rs

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