sqlite_containers/
common.rs1use crate::{
6 rusqlite::{
7 Error as RusqliteError, ErrorCode, OpenFlags,
8 types::{FromSql, ToSql, Type as RusqliteType},
9 version_number,
10 },
11 semver::Version,
12};
13use cfg_if::cfg_if;
14use std::{fmt::Display, sync::LazyLock};
15
16pub type Blob = Vec<u8>;
18
19pub static OPEN_FLAGS: LazyLock<OpenFlags> = LazyLock::new(|| {
21 let mut flags = OpenFlags::default();
22 flags.remove(OpenFlags::SQLITE_OPEN_URI);
23 flags
24});
25
26trait Sealed {}
31
32macro_rules! impl_sealed {
33 ($($t:ty),*) => {
34 $(
35 impl Sealed for $t {}
36 )*
37 };
38}
39
40impl_sealed!(i32, u32, i64, String, Blob);
41
42#[derive(Eq, PartialEq, Copy, Clone, Debug)]
47pub struct SqlType(pub RusqliteType);
48
49impl SqlType {
50 #[inline(always)]
51 pub const fn new(value: RusqliteType) -> Self {
52 Self(value)
53 }
54}
55
56impl Ord for SqlType {
57 #[inline]
58 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
59 (self.0 as usize).cmp(&(other.0 as usize))
60 }
61}
62
63impl PartialOrd for SqlType {
64 #[inline]
65 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66 Some(self.cmp(other))
67 }
68}
69
70impl Display for SqlType {
71 #[inline]
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 f.write_str(&self.0.to_string().to_ascii_uppercase())
74 }
75}
76
77pub const SQL_TYPE_TEXT: SqlType = SqlType::new(RusqliteType::Text);
78pub static SQL_TYPES: [SqlType; 3usize] = [SqlType::new(RusqliteType::Integer), SqlType::new(RusqliteType::Text), SqlType::new(RusqliteType::Blob)];
79
80#[allow(private_bounds)]
104pub trait Type: FromSql + ToSql + Sealed {
105 const SQL_TYPE: SqlType;
107}
108
109macro_rules! impl_type {
110 ($type:ty, $sql_type:ident) => {
111 impl Type for $type {
112 const SQL_TYPE: SqlType = SqlType::new(RusqliteType::$sql_type);
113 }
114 };
115}
116
117impl_type!(i32, Integer);
118impl_type!(u32, Integer);
119impl_type!(i64, Integer);
120impl_type!(Blob, Blob);
121impl_type!(String, Text);
122
123pub trait CharType {}
131
132macro_rules! impl_chartype {
133 ($($t:ty),*) => {
134 $(
135 impl CharType for $t {}
136 )*
137 };
138}
139
140impl_chartype!(String);
141
142cfg_if! {
147 if #[cfg(target_pointer_width = "64")] {
148 pub type Size = usize;
150 } else {
151 pub type Size = u64;
153 }
154}
155
156pub trait SizeExt {
158 fn from_i64(value: i64) -> Self;
159 fn from_u64(value: u64) -> Self;
160 fn to_u64(self) -> u64;
161 fn to_usize(self) -> Option<usize>;
162}
163
164impl SizeExt for Size {
165 #[inline(always)]
166 fn from_i64(value: i64) -> Self {
167 value.try_into().unwrap_or_default()
168 }
169
170 #[inline(always)]
171 fn from_u64(value: u64) -> Self {
172 value as Self
173 }
174
175 #[allow(clippy::unnecessary_cast)]
176 #[inline(always)]
177 fn to_u64(self) -> u64 {
178 self as u64
179 }
180
181 #[inline(always)]
182 fn to_usize(self) -> Option<usize> {
183 cfg_if! {
184 if #[cfg(target_pointer_width = "64")] {
185 Some(self)
186 } else {
187 self.try_into().ok()
188 }
189 }
190 }
191}
192
193#[allow(dead_code)]
203#[derive(Debug)]
204pub struct Error(RusqliteError);
205
206impl Error {
207 pub fn into_inner(self) -> RusqliteError {
208 self.0
209 }
210}
211
212impl From<RusqliteError> for Error {
213 #[inline]
214 fn from(error: RusqliteError) -> Self {
215 Self(error)
216 }
217}
218
219#[inline]
220pub fn check_constraint_violation(error: RusqliteError) -> Result<bool, Error> {
221 match error {
222 RusqliteError::SqliteFailure(sql_error, _) => match sql_error.code {
223 ErrorCode::ConstraintViolation => Ok(false),
224 _ => Err(error.into()),
225 },
226 _ => Err(error.into()),
227 }
228}
229
230pub fn version() -> &'static Version {
232 static PKG_VERSION: LazyLock<Version> = LazyLock::new(|| Version::parse(env!["CARGO_PKG_VERSION"]).expect("Failed to parse version!"));
233 &PKG_VERSION
234}
235
236pub fn sqlite_version() -> &'static Version {
238 static SQLITE_VERSION: LazyLock<Version> = LazyLock::new(|| {
239 let version = version_number() as u64;
240 Version::new(version / 1000000u64, (version % 1000000u64) / 1000u64, version % 1000u64)
241 });
242 &SQLITE_VERSION
243}
244
245#[cfg(test)]
250mod test {
251 use super::*;
252 use crate::rusqlite::ffi::{Error as FfiError, ErrorCode as FfiErrorCode, SQLITE_CONSTRAINT, SQLITE_NOMEM};
253 use std::path::PathBuf;
254
255 #[cfg(target_pointer_width = "64")]
256 #[test]
257 fn test_size_p64() {
258 assert_eq!(Size::from_i64(i64::MAX), i64::MAX as usize);
259 assert_eq!(Size::from_i64(1i64), 1usize);
260 assert_eq!(Size::from_i64(0i64), usize::MIN);
261 assert_eq!(Size::from_i64(-1i64), usize::MIN);
262 assert_eq!(Size::from_i64(i64::MIN), usize::MIN);
263
264 assert_eq!(Size::from_u64(u64::MAX), usize::MAX);
265 assert_eq!(Size::from_u64(1u64), 1usize);
266 assert_eq!(Size::from_u64(u64::MIN), usize::MIN);
267
268 assert_eq!(Size::from_i64(i64::MAX).to_u64(), i64::MAX as u64);
269 assert_eq!(Size::from_i64(i64::MAX).to_usize(), Some(i64::MAX as usize));
270 }
271
272 #[cfg(not(target_pointer_width = "64"))]
273 #[test]
274 fn test_size_p32() {
275 assert_eq!(Size::from_i64(i64::MAX), i64::MAX as u64);
276 assert_eq!(Size::from_i64(1i64), 1u64);
277 assert_eq!(Size::from_i64(0i64), u64::MIN);
278 assert_eq!(Size::from_i64(-1i64), u64::MIN);
279 assert_eq!(Size::from_i64(i64::MIN), u64::MIN);
280
281 assert_eq!(Size::from_u64(u64::MAX), u64::MAX);
282 assert_eq!(Size::from_u64(1u64), 1u64);
283 assert_eq!(Size::from_u64(u64::MIN), u64::MIN);
284
285 assert_eq!(Size::from_i64(i64::MAX).to_u64(), i64::MAX as u64);
286 assert_eq!(Size::from_i64(usize::MAX as i64).to_usize(), Some(usize::MAX));
287 assert!(Size::from_i64(i64::MAX).to_usize().is_none());
288 }
289
290 #[test]
291 fn test_sql_type() {
292 const ALL_TYPES: [RusqliteType; 3usize] = [RusqliteType::Integer, RusqliteType::Blob, RusqliteType::Text];
293 for native_type in ALL_TYPES {
294 let sql_type = SqlType::new(native_type);
295 assert_eq!(sql_type.0, native_type);
296 for other_type in ALL_TYPES {
297 let other_type = SqlType::new(other_type);
298 assert_eq!(sql_type.partial_cmp(&other_type).unwrap(), sql_type.cmp(&other_type));
299 }
300 }
301 }
302
303 #[test]
304 fn test_error() {
305 let error_1 = Error::from(RusqliteError::InvalidQuery);
306 assert_eq!(error_1.into_inner(), RusqliteError::InvalidQuery);
307
308 let error_2 = Error::from(RusqliteError::InvalidPath(PathBuf::from("filename.txt")));
309 assert!(matches!(error_2.into_inner(), RusqliteError::InvalidPath(_)));
310
311 let error_3 = Error::from(RusqliteError::SqliteFailure(FfiError::new(SQLITE_NOMEM), Some("Memory allocation fail!".to_owned())));
312 assert!(matches!(error_3.into_inner(), RusqliteError::SqliteFailure(FfiError { code: FfiErrorCode::OutOfMemory, .. }, Some(_))));
313 }
314
315 #[test]
316 fn test_check_constraint_violation() {
317 let error_1 = check_constraint_violation(RusqliteError::InvalidQuery);
318 assert!(matches!(error_1, Err(Error(..))));
319
320 let error_2 = check_constraint_violation(RusqliteError::SqliteFailure(FfiError::new(SQLITE_CONSTRAINT), None));
321 assert!(matches!(error_2, Ok(false)));
322
323 let error_3 = check_constraint_violation(RusqliteError::SqliteFailure(FfiError::new(SQLITE_NOMEM), None));
324 assert!(matches!(error_3, Err(Error(..))));
325 }
326
327 #[test]
328 fn test_get_version() {
329 let (pkg_version, sqlite_version) = (version(), sqlite_version());
330 eprintln!("pkg_version: {pkg_version:?}");
331 eprintln!("sqlite_version: {sqlite_version:?}");
332
333 assert_eq!(pkg_version.to_string(), env!["CARGO_PKG_VERSION"]);
334 assert!(
335 (sqlite_version.major > 3)
336 || ((sqlite_version.major == 3) && (sqlite_version.minor > 34))
337 || ((sqlite_version.major == 3) && (sqlite_version.minor == 34) && (sqlite_version.minor >= 1))
338 );
339 }
340}