1use super::{ffi, sqlite3_match_version, sqlite3_require_version, types::*};
2use std::{cmp::Ordering, ffi::CStr, str};
3
4pub struct SqliteVersion;
6
7pub static SQLITE_VERSION: SqliteVersion = SqliteVersion;
9
10impl SqliteVersion {
11 pub fn as_i32(&self) -> i32 {
17 unsafe { ffi::sqlite3_libversion_number() }
18 }
19
20 pub fn as_str(&self) -> &'static str {
22 let ret = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
23 ret.to_str().expect("sqlite3_libversion")
24 }
25
26 pub fn sourceid(&self) -> Result<&'static str> {
31 sqlite3_require_version!(3_021_000, {
32 let ret = unsafe { CStr::from_ptr(ffi::sqlite3_sourceid()) };
33 Ok(ret.to_str().expect("sqlite3_sourceid"))
34 })
35 }
36}
37
38impl std::fmt::Display for SqliteVersion {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 self.as_str().fmt(f)
41 }
42}
43
44pub fn sqlite3_stricmp(a: &str, b: &str) -> Ordering {
49 sqlite3_match_version! {
50 3_006_017 => {
51 let rc = unsafe {
52 ffi::sqlite3_strnicmp(a.as_ptr() as _, b.as_ptr() as _, std::cmp::min(a.len(), b.len()) as _)
53 };
54 if rc < 0 {
55 Ordering::Less
56 } else if rc > 0 {
57 Ordering::Greater
58 } else {
59 Ordering::Equal
60 }
61 }
62 _ => a
63 .bytes()
64 .zip(b.bytes())
65 .find_map(|(a, b)| match a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()) {
66 Ordering::Equal => None,
67 x => Some(x),
68 })
69 .unwrap_or(a.len().cmp(&b.len())),
70 }
71}
72
73pub fn sqlite3_strglob(pattern: impl Into<Vec<u8>>, input: impl Into<Vec<u8>>) -> Result<bool> {
77 let _ = (&pattern, &input);
78 sqlite3_require_version!(3_007_017, {
79 let pattern = std::ffi::CString::new(pattern)?;
80 let input = std::ffi::CString::new(input)?;
81 Ok(unsafe { ffi::sqlite3_strglob(pattern.as_ptr(), input.as_ptr()) == 0 })
82 })
83}
84
85pub fn sqlite3_strlike(
91 pattern: impl Into<Vec<u8>>,
92 input: impl Into<Vec<u8>>,
93 escape: impl Into<char>,
94) -> Result<bool> {
95 let _ = (&pattern, &input, &escape);
96 sqlite3_require_version!(3_010_000, {
97 let pattern = std::ffi::CString::new(pattern)?;
98 let input = std::ffi::CString::new(input)?;
99 let escape = escape.into();
100 let escape: u32 = if escape.is_ascii() { escape as _ } else { 0 };
101 Ok(unsafe { ffi::sqlite3_strlike(pattern.as_ptr(), input.as_ptr(), escape) == 0 })
102 })
103}
104
105pub fn sqlite3_randomness(n: usize) -> Vec<u8> {
106 let mut ret = vec![0; n];
107 unsafe { ffi::sqlite3_randomness(n as _, ret.as_mut_ptr() as _) };
108 ret
109}
110
111#[cfg(all(test, feature = "static"))]
112mod test {
113 use super::*;
114
115 #[test]
116 fn version() -> Result<()> {
117 assert!(SqliteVersion.as_i32() > 3_000_000);
118 assert!(format!("{}", SqliteVersion).len() >= 5);
120 sqlite3_match_version! {
121 3_021_000 => assert!(SqliteVersion.sourceid()?.len() > 0),
122 _ => (),
123 }
124 Ok(())
125 }
126
127 #[test]
128 fn strings() -> Result<()> {
129 assert_eq!(sqlite3_stricmp("FOO", "bar"), Ordering::Greater);
130 assert_eq!(sqlite3_stricmp("bar", "FOO"), Ordering::Less);
131 assert_eq!(sqlite3_stricmp("bar", "BAR"), Ordering::Equal);
132 sqlite3_match_version! {
133 3_007_017 => assert_eq!(sqlite3_strglob("a/**/b", "a/c/d/e/f/b"), Ok(true)),
134 _ => (),
135 }
136 sqlite3_match_version! {
137 3_010_000 => {
138 assert_eq!(sqlite3_strlike("FOO\\_BAR", "FOO_BAR", '\\'), Ok(true));
139 assert_eq!(sqlite3_strlike("FOO_BAR", "FOOXBAR", 0), Ok(true));
140 }
141 _ => (),
142 }
143 Ok(())
144 }
145
146 #[test]
147 fn randomness() {
148 let ret = sqlite3_randomness(32);
149 assert_eq!(ret.len(), 32);
150 assert_ne!(ret, vec![0; 32]);
151 }
152}