shared/
prelude.rs

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    /// Returns Some if the string has content, otherwise None.
105    fn optional(&self) -> Option<&Self>;
106
107    /// Returns Some if the string has content, otherwise None.
108    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}