use super::{ffi, sqlite3_match_version, sqlite3_require_version, types::*};
use std::{cmp::Ordering, ffi::CStr, str};
pub struct SqliteVersion;
pub static SQLITE_VERSION: SqliteVersion = SqliteVersion;
impl SqliteVersion {
pub fn as_i32(&self) -> i32 {
unsafe { ffi::sqlite3_libversion_number() }
}
pub fn as_str(&self) -> &'static str {
let ret = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
ret.to_str().expect("sqlite3_libversion")
}
pub fn sourceid(&self) -> Result<&'static str> {
sqlite3_require_version!(3_021_000, {
let ret = unsafe { CStr::from_ptr(ffi::sqlite3_sourceid()) };
Ok(ret.to_str().expect("sqlite3_sourceid"))
})
}
}
impl std::fmt::Display for SqliteVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
pub fn sqlite3_stricmp(a: &str, b: &str) -> Ordering {
sqlite3_match_version! {
3_006_017 => {
let rc = unsafe {
ffi::sqlite3_strnicmp(a.as_ptr() as _, b.as_ptr() as _, std::cmp::min(a.len(), b.len()) as _)
};
if rc < 0 {
Ordering::Less
} else if rc > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
_ => a
.bytes()
.zip(b.bytes())
.find_map(|(a, b)| match a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()) {
Ordering::Equal => None,
x => Some(x),
})
.unwrap_or(a.len().cmp(&b.len())),
}
}
pub fn sqlite3_strglob(pattern: impl Into<Vec<u8>>, input: impl Into<Vec<u8>>) -> Result<bool> {
let _ = (&pattern, &input);
sqlite3_require_version!(3_007_017, {
let pattern = std::ffi::CString::new(pattern)?;
let input = std::ffi::CString::new(input)?;
Ok(unsafe { ffi::sqlite3_strglob(pattern.as_ptr(), input.as_ptr()) == 0 })
})
}
pub fn sqlite3_strlike(
pattern: impl Into<Vec<u8>>,
input: impl Into<Vec<u8>>,
escape: impl Into<char>,
) -> Result<bool> {
let _ = (&pattern, &input, &escape);
sqlite3_require_version!(3_010_000, {
let pattern = std::ffi::CString::new(pattern)?;
let input = std::ffi::CString::new(input)?;
let escape = escape.into();
let escape: u32 = if escape.is_ascii() { escape as _ } else { 0 };
Ok(unsafe { ffi::sqlite3_strlike(pattern.as_ptr(), input.as_ptr(), escape) == 0 })
})
}
pub fn sqlite3_randomness(n: usize) -> Vec<u8> {
let mut ret = vec![0; n];
unsafe { ffi::sqlite3_randomness(n as _, ret.as_mut_ptr() as _) };
ret
}
#[cfg(all(test, feature = "static"))]
mod test {
use super::*;
#[test]
fn version() -> Result<()> {
assert!(SqliteVersion.as_i32() > 3_000_000);
assert!(format!("{}", SqliteVersion).len() >= 5);
sqlite3_match_version! {
3_021_000 => assert!(SqliteVersion.sourceid()?.len() > 0),
_ => (),
}
Ok(())
}
#[test]
fn strings() -> Result<()> {
assert_eq!(sqlite3_stricmp("FOO", "bar"), Ordering::Greater);
assert_eq!(sqlite3_stricmp("bar", "FOO"), Ordering::Less);
assert_eq!(sqlite3_stricmp("bar", "BAR"), Ordering::Equal);
sqlite3_match_version! {
3_007_017 => assert_eq!(sqlite3_strglob("a/**/b", "a/c/d/e/f/b"), Ok(true)),
_ => (),
}
sqlite3_match_version! {
3_010_000 => {
assert_eq!(sqlite3_strlike("FOO\\_BAR", "FOO_BAR", '\\'), Ok(true));
assert_eq!(sqlite3_strlike("FOO_BAR", "FOOXBAR", 0), Ok(true));
}
_ => (),
}
Ok(())
}
#[test]
fn randomness() {
let ret = sqlite3_randomness(32);
assert_eq!(ret.len(), 32);
assert_ne!(ret, vec![0; 32]);
}
}