diff options
Diffstat (limited to 'src/types/value_ref.rs')
-rw-r--r-- | src/types/value_ref.rs | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs new file mode 100644 index 0000000..80d2457 --- /dev/null +++ b/src/types/value_ref.rs @@ -0,0 +1,170 @@ +use super::{Type, Value}; +use crate::types::{FromSqlError, FromSqlResult}; + +/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the +/// memory backing this value is owned by SQLite. +/// +/// See [`Value`](enum.Value.html) for an owning dynamic type value. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ValueRef<'a> { + /// The value is a `NULL` value. + Null, + /// The value is a signed integer. + Integer(i64), + /// The value is a floating point number. + Real(f64), + /// The value is a text string. + Text(&'a [u8]), + /// The value is a blob of data + Blob(&'a [u8]), +} + +impl ValueRef<'_> { + pub fn data_type(&self) -> Type { + match *self { + ValueRef::Null => Type::Null, + ValueRef::Integer(_) => Type::Integer, + ValueRef::Real(_) => Type::Real, + ValueRef::Text(_) => Type::Text, + ValueRef::Blob(_) => Type::Blob, + } + } +} + +impl<'a> ValueRef<'a> { + /// If `self` is case `Integer`, returns the integral value. Otherwise, + /// returns `Err(Error::InvalidColumnType)`. + pub fn as_i64(&self) -> FromSqlResult<i64> { + match *self { + ValueRef::Integer(i) => Ok(i), + _ => Err(FromSqlError::InvalidType), + } + } + + /// If `self` is case `Real`, returns the floating point value. Otherwise, + /// returns `Err(Error::InvalidColumnType)`. + pub fn as_f64(&self) -> FromSqlResult<f64> { + match *self { + ValueRef::Real(f) => Ok(f), + _ => Err(FromSqlError::InvalidType), + } + } + + /// If `self` is case `Text`, returns the string value. Otherwise, returns + /// `Err(Error::InvalidColumnType)`. + pub fn as_str(&self) -> FromSqlResult<&'a str> { + match *self { + ValueRef::Text(t) => { + std::str::from_utf8(t).map_err(|e| FromSqlError::Other(Box::new(e))) + } + _ => Err(FromSqlError::InvalidType), + } + } + + /// If `self` is case `Blob`, returns the byte slice. Otherwise, returns + /// `Err(Error::InvalidColumnType)`. + pub fn as_blob(&self) -> FromSqlResult<&'a [u8]> { + match *self { + ValueRef::Blob(b) => Ok(b), + _ => Err(FromSqlError::InvalidType), + } + } +} + +impl From<ValueRef<'_>> for Value { + fn from(borrowed: ValueRef<'_>) -> Value { + match borrowed { + ValueRef::Null => Value::Null, + ValueRef::Integer(i) => Value::Integer(i), + ValueRef::Real(r) => Value::Real(r), + ValueRef::Text(s) => { + let s = std::str::from_utf8(s).expect("invalid UTF-8"); + Value::Text(s.to_string()) + } + ValueRef::Blob(b) => Value::Blob(b.to_vec()), + } + } +} + +impl<'a> From<&'a str> for ValueRef<'a> { + fn from(s: &str) -> ValueRef<'_> { + ValueRef::Text(s.as_bytes()) + } +} + +impl<'a> From<&'a [u8]> for ValueRef<'a> { + fn from(s: &[u8]) -> ValueRef<'_> { + ValueRef::Blob(s) + } +} + +impl<'a> From<&'a Value> for ValueRef<'a> { + fn from(value: &'a Value) -> ValueRef<'a> { + match *value { + Value::Null => ValueRef::Null, + Value::Integer(i) => ValueRef::Integer(i), + Value::Real(r) => ValueRef::Real(r), + Value::Text(ref s) => ValueRef::Text(s.as_bytes()), + Value::Blob(ref b) => ValueRef::Blob(b), + } + } +} + +impl<'a, T> From<Option<T>> for ValueRef<'a> +where + T: Into<ValueRef<'a>>, +{ + fn from(s: Option<T>) -> ValueRef<'a> { + match s { + Some(x) => x.into(), + None => ValueRef::Null, + } + } +} + +#[cfg(any(feature = "functions", feature = "session", feature = "vtab"))] +impl<'a> ValueRef<'a> { + pub(crate) unsafe fn from_value(value: *mut crate::ffi::sqlite3_value) -> ValueRef<'a> { + use crate::ffi; + use std::slice::from_raw_parts; + + match ffi::sqlite3_value_type(value) { + ffi::SQLITE_NULL => ValueRef::Null, + ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)), + ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)), + ffi::SQLITE_TEXT => { + let text = ffi::sqlite3_value_text(value); + let len = ffi::sqlite3_value_bytes(value); + assert!( + !text.is_null(), + "unexpected SQLITE_TEXT value type with NULL data" + ); + let s = from_raw_parts(text as *const u8, len as usize); + ValueRef::Text(s) + } + ffi::SQLITE_BLOB => { + let (blob, len) = ( + ffi::sqlite3_value_blob(value), + ffi::sqlite3_value_bytes(value), + ); + + assert!( + len >= 0, + "unexpected negative return from sqlite3_value_bytes" + ); + if len > 0 { + assert!( + !blob.is_null(), + "unexpected SQLITE_BLOB value type with NULL data" + ); + ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) + } else { + // The return value from sqlite3_value_blob() for a zero-length BLOB + // is a NULL pointer. + ValueRef::Blob(&[]) + } + } + _ => unreachable!("sqlite3_value_type returned invalid value"), + } + } +} |