use std::str::FromStr; use crate::{Error, Result}; #[derive(Clone, Debug, PartialEq)] pub enum Value { Null, Integer(i64), Real(f64), Text(String), Blob(Vec), } /// The possible types a column can be in libsql. #[derive(Debug, Copy, Clone)] pub enum ValueType { Integer = 1, Real, Text, Blob, Null, } impl FromStr for ValueType { type Err = (); fn from_str(s: &str) -> std::result::Result { match s { "TEXT" => Ok(ValueType::Text), "INTEGER" => Ok(ValueType::Integer), "BLOB" => Ok(ValueType::Blob), "NULL" => Ok(ValueType::Null), "REAL" => Ok(ValueType::Real), _ => Err(()), } } } impl Value { /// Returns `true` if the value is [`Null`]. /// /// [`Null`]: Value::Null #[must_use] pub fn is_null(&self) -> bool { matches!(self, Self::Null) } /// Returns `true` if the value is [`Integer`]. /// /// [`Integer`]: Value::Integer #[must_use] pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(..)) } /// Returns `true` if the value is [`Real`]. /// /// [`Real`]: Value::Real #[must_use] pub fn is_real(&self) -> bool { matches!(self, Self::Real(..)) } pub fn as_real(&self) -> Option<&f64> { if let Self::Real(v) = self { Some(v) } else { None } } /// Returns `true` if the value is [`Text`]. /// /// [`Text`]: Value::Text #[must_use] pub fn is_text(&self) -> bool { matches!(self, Self::Text(..)) } pub fn as_text(&self) -> Option<&String> { if let Self::Text(v) = self { Some(v) } else { None } } pub fn as_integer(&self) -> Option<&i64> { if let Self::Integer(v) = self { Some(v) } else { None } } /// Returns `true` if the value is [`Blob`]. /// /// [`Blob`]: Value::Blob #[must_use] pub fn is_blob(&self) -> bool { matches!(self, Self::Blob(..)) } pub fn as_blob(&self) -> Option<&Vec> { if let Self::Blob(v) = self { Some(v) } else { None } } } impl From for Value { fn from(val: turso_core::Value) -> Self { match val { turso_core::Value::Null => Value::Null, turso_core::Value::Integer(n) => Value::Integer(n), turso_core::Value::Float(n) => Value::Real(n), turso_core::Value::Text(t) => Value::Text(t.into()), turso_core::Value::Blob(items) => Value::Blob(items), } } } impl From for turso_core::Value { fn from(val: Value) -> Self { match val { Value::Null => turso_core::Value::Null, Value::Integer(n) => turso_core::Value::Integer(n), Value::Real(n) => turso_core::Value::Float(n), Value::Text(t) => turso_core::Value::from_text(&t), Value::Blob(items) => turso_core::Value::from_blob(items), } } } impl From for Value { fn from(value: i8) -> Value { Value::Integer(value as i64) } } impl From for Value { fn from(value: i16) -> Value { Value::Integer(value as i64) } } impl From for Value { fn from(value: i32) -> Value { Value::Integer(value as i64) } } impl From for Value { fn from(value: i64) -> Value { Value::Integer(value) } } impl From for Value { fn from(value: u8) -> Value { Value::Integer(value as i64) } } impl From for Value { fn from(value: u16) -> Value { Value::Integer(value as i64) } } impl From for Value { fn from(value: u32) -> Value { Value::Integer(value as i64) } } impl TryFrom for Value { type Error = Error; fn try_from(value: u64) -> Result { if value > i64::MAX as u64 { Err(Error::ToSqlConversionFailure( "u64 is too large to fit in an i64".into(), )) } else { Ok(Value::Integer(value as i64)) } } } impl From for Value { fn from(value: f32) -> Value { Value::Real(value as f64) } } impl From for Value { fn from(value: f64) -> Value { Value::Real(value) } } impl From<&str> for Value { fn from(value: &str) -> Value { Value::Text(value.to_owned()) } } impl From for Value { fn from(value: String) -> Value { Value::Text(value) } } impl From<&[u8]> for Value { fn from(value: &[u8]) -> Value { Value::Blob(value.to_owned()) } } impl From> for Value { fn from(value: Vec) -> Value { Value::Blob(value) } } impl From for Value { fn from(value: bool) -> Value { Value::Integer(value as i64) } } impl From> for Value where T: Into, { fn from(value: Option) -> Self { match value { Some(inner) => inner.into(), None => Value::Null, } } } /// A borrowed version of `Value`. #[derive(Debug)] pub enum ValueRef<'a> { Null, Integer(i64), Real(f64), Text(&'a [u8]), Blob(&'a [u8]), } impl ValueRef<'_> { pub fn data_type(&self) -> ValueType { match *self { ValueRef::Null => ValueType::Null, ValueRef::Integer(_) => ValueType::Integer, ValueRef::Real(_) => ValueType::Real, ValueRef::Text(_) => ValueType::Text, ValueRef::Blob(_) => ValueType::Blob, } } /// Returns `true` if the value ref is [`Null`]. /// /// [`Null`]: ValueRef::Null #[must_use] pub fn is_null(&self) -> bool { matches!(self, Self::Null) } /// Returns `true` if the value ref is [`Integer`]. /// /// [`Integer`]: ValueRef::Integer #[must_use] pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(..)) } pub fn as_integer(&self) -> Option<&i64> { if let Self::Integer(v) = self { Some(v) } else { None } } /// Returns `true` if the value ref is [`Real`]. /// /// [`Real`]: ValueRef::Real #[must_use] pub fn is_real(&self) -> bool { matches!(self, Self::Real(..)) } pub fn as_real(&self) -> Option<&f64> { if let Self::Real(v) = self { Some(v) } else { None } } /// Returns `true` if the value ref is [`Text`]. /// /// [`Text`]: ValueRef::Text #[must_use] pub fn is_text(&self) -> bool { matches!(self, Self::Text(..)) } pub fn as_text(&self) -> Option<&[u8]> { if let Self::Text(v) = self { Some(v) } else { None } } /// Returns `true` if the value ref is [`Blob`]. /// /// [`Blob`]: ValueRef::Blob #[must_use] pub fn is_blob(&self) -> bool { matches!(self, Self::Blob(..)) } pub fn as_blob(&self) -> Option<&[u8]> { if let Self::Blob(v) = self { Some(v) } else { None } } } impl From> for Value { fn from(vr: ValueRef<'_>) -> Value { match vr { ValueRef::Null => Value::Null, ValueRef::Integer(i) => Value::Integer(i), ValueRef::Real(r) => Value::Real(r), ValueRef::Text(s) => Value::Text(String::from_utf8_lossy(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(v: &'a Value) -> ValueRef<'a> { match *v { 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> for ValueRef<'a> where T: Into>, { #[inline] fn from(s: Option) -> ValueRef<'a> { match s { Some(x) => x.into(), None => ValueRef::Null, } } }