sqlite_containers/
common.rs1use crate::rusqlite::{
6 Error as RusqliteError, ErrorCode,
7 types::{FromSql, ToSql, Type as RusqliteType},
8};
9use cfg_if::cfg_if;
10use std::fmt::Display;
11
12pub type Blob = Vec<u8>;
14
15trait Sealed {}
20
21macro_rules! impl_sealed {
22 ($($t:ty),*) => {
23 $(
24 impl Sealed for $t {}
25 )*
26 };
27}
28
29impl_sealed!(i32, u32, i64, String, Blob);
30
31#[derive(Eq, PartialEq, Copy, Clone, Debug)]
36pub struct SqlType(pub RusqliteType);
37
38impl SqlType {
39 #[inline(always)]
40 pub const fn new(value: RusqliteType) -> Self {
41 Self(value)
42 }
43}
44
45impl Ord for SqlType {
46 #[inline]
47 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
48 (self.0 as usize).cmp(&(other.0 as usize))
49 }
50}
51
52impl PartialOrd for SqlType {
53 #[inline]
54 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
55 Some(self.cmp(other))
56 }
57}
58
59impl Display for SqlType {
60 #[inline]
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.write_str(&self.0.to_string().to_ascii_uppercase())
63 }
64}
65
66pub const SQL_TYPE_TEXT: SqlType = SqlType::new(RusqliteType::Text);
67pub static SQL_TYPES: [SqlType; 3usize] = [SqlType::new(RusqliteType::Integer), SqlType::new(RusqliteType::Text), SqlType::new(RusqliteType::Blob)];
68
69#[allow(private_bounds)]
93pub trait Type: FromSql + ToSql + Sealed {
94 const SQL_TYPE: SqlType;
96}
97
98macro_rules! impl_type {
99 ($type:ty, $sql_type:ident) => {
100 impl Type for $type {
101 const SQL_TYPE: SqlType = SqlType::new(RusqliteType::$sql_type);
102 }
103 };
104}
105
106impl_type!(i32, Integer);
107impl_type!(u32, Integer);
108impl_type!(i64, Integer);
109impl_type!(Blob, Blob);
110impl_type!(String, Text);
111
112pub trait CharType {}
120
121macro_rules! impl_chartype {
122 ($($t:ty),*) => {
123 $(
124 impl CharType for $t {}
125 )*
126 };
127}
128
129impl_chartype!(String);
130
131cfg_if! {
136 if #[cfg(target_pointer_width = "64")] {
137 pub type Size = usize;
139 } else {
140 pub type Size = u64;
142 }
143}
144
145pub trait SizeExt {
147 fn from_i64(value: i64) -> Self;
148 fn from_u64(value: u64) -> Self;
149 fn to_u64(self) -> u64;
150 fn to_usize(self) -> Option<usize>;
151}
152
153impl SizeExt for Size {
154 #[inline(always)]
155 fn from_i64(value: i64) -> Self {
156 value.try_into().unwrap_or_default()
157 }
158
159 #[inline(always)]
160 fn from_u64(value: u64) -> Self {
161 value as Self
162 }
163
164 #[allow(clippy::unnecessary_cast)]
165 #[inline(always)]
166 fn to_u64(self) -> u64 {
167 self as u64
168 }
169
170 #[inline(always)]
171 fn to_usize(self) -> Option<usize> {
172 cfg_if! {
173 if #[cfg(target_pointer_width = "64")] {
174 Some(self)
175 } else {
176 self.try_into().ok()
177 }
178 }
179 }
180}
181
182#[allow(dead_code)]
192#[derive(Debug)]
193pub struct Error(RusqliteError);
194
195impl Error {
196 pub fn into_inner(self) -> RusqliteError {
197 self.0
198 }
199}
200
201impl From<RusqliteError> for Error {
202 #[inline]
203 fn from(error: RusqliteError) -> Self {
204 Self(error)
205 }
206}
207
208#[inline]
209pub fn check_constraint_violation(error: RusqliteError) -> Result<bool, Error> {
210 match error {
211 RusqliteError::SqliteFailure(sql_error, _) => match sql_error.code {
212 ErrorCode::ConstraintViolation => Ok(false),
213 _ => Err(error.into()),
214 },
215 _ => Err(error.into()),
216 }
217}
218
219#[cfg(test)]
224mod test {
225 use super::*;
226 use crate::rusqlite::ffi::{Error as FfiError, ErrorCode as FfiErrorCode, SQLITE_CONSTRAINT, SQLITE_NOMEM};
227 use std::path::PathBuf;
228
229 #[cfg(target_pointer_width = "64")]
230 #[test]
231 fn test_size_p64() {
232 assert_eq!(Size::from_i64(i64::MAX), i64::MAX as usize);
233 assert_eq!(Size::from_i64(1i64), 1usize);
234 assert_eq!(Size::from_i64(0i64), usize::MIN);
235 assert_eq!(Size::from_i64(-1i64), usize::MIN);
236 assert_eq!(Size::from_i64(i64::MIN), usize::MIN);
237
238 assert_eq!(Size::from_u64(u64::MAX), usize::MAX);
239 assert_eq!(Size::from_u64(1u64), 1usize);
240 assert_eq!(Size::from_u64(u64::MIN), usize::MIN);
241
242 assert_eq!(Size::from_i64(i64::MAX).to_u64(), i64::MAX as u64);
243 assert_eq!(Size::from_i64(i64::MAX).to_usize(), Some(i64::MAX as usize));
244 }
245
246 #[cfg(not(target_pointer_width = "64"))]
247 #[test]
248 fn test_size_p32() {
249 assert_eq!(Size::from_i64(i64::MAX), i64::MAX as u64);
250 assert_eq!(Size::from_i64(1i64), 1u64);
251 assert_eq!(Size::from_i64(0i64), u64::MIN);
252 assert_eq!(Size::from_i64(-1i64), u64::MIN);
253 assert_eq!(Size::from_i64(i64::MIN), u64::MIN);
254
255 assert_eq!(Size::from_u64(u64::MAX), u64::MAX);
256 assert_eq!(Size::from_u64(1u64), 1u64);
257 assert_eq!(Size::from_u64(u64::MIN), u64::MIN);
258
259 assert_eq!(Size::from_i64(i64::MAX).to_u64(), i64::MAX as u64);
260 assert_eq!(Size::from_i64(usize::MAX as i64).to_usize(), Some(usize::MAX));
261 assert!(Size::from_i64(i64::MAX).to_usize().is_none());
262 }
263
264 #[test]
265 fn test_sql_type() {
266 const ALL_TYPES: [RusqliteType; 3usize] = [RusqliteType::Integer, RusqliteType::Blob, RusqliteType::Text];
267 for native_type in ALL_TYPES {
268 let sql_type = SqlType::new(native_type);
269 assert_eq!(sql_type.0, native_type);
270 for other_type in ALL_TYPES {
271 let other_type = SqlType::new(other_type);
272 assert_eq!(sql_type.partial_cmp(&other_type).unwrap(), sql_type.cmp(&other_type));
273 }
274 }
275 }
276
277 #[test]
278 fn test_error() {
279 let error_1 = Error::from(RusqliteError::InvalidQuery);
280 assert_eq!(error_1.into_inner(), RusqliteError::InvalidQuery);
281
282 let error_2 = Error::from(RusqliteError::InvalidPath(PathBuf::from("filename.txt")));
283 assert!(matches!(error_2.into_inner(), RusqliteError::InvalidPath(_)));
284
285 let error_3 = Error::from(RusqliteError::SqliteFailure(FfiError::new(SQLITE_NOMEM), Some("Memory allocation fail!".to_owned())));
286 assert!(matches!(error_3.into_inner(), RusqliteError::SqliteFailure(FfiError { code: FfiErrorCode::OutOfMemory, .. }, Some(_))));
287 }
288
289 #[test]
290 fn test_check_constraint_violation() {
291 let error_1 = check_constraint_violation(RusqliteError::InvalidQuery);
292 assert!(matches!(error_1, Err(Error(..))));
293
294 let error_2 = check_constraint_violation(RusqliteError::SqliteFailure(FfiError::new(SQLITE_CONSTRAINT), None));
295 assert!(matches!(error_2, Ok(false)));
296
297 let error_3 = check_constraint_violation(RusqliteError::SqliteFailure(FfiError::new(SQLITE_NOMEM), None));
298 assert!(matches!(error_3, Err(Error(..))));
299 }
300}