use crate::database::error::DbError;
#[derive(Clone, Debug, PartialEq)]
pub enum SqlValue {
Null,
Integer(i64),
Real(f64),
Text(String),
}
impl SqlValue {
pub fn type_name(&self) -> &'static str {
match self {
SqlValue::Null => "Null",
SqlValue::Integer(_) => "Integer",
SqlValue::Real(_) => "Real",
SqlValue::Text(_) => "Text",
}
}
pub fn as_i64(&self) -> Result<i64, DbError> {
match self {
SqlValue::Integer(i) => Ok(*i),
SqlValue::Null => Err(DbError::NullValue("?".into())),
other => Err(DbError::TypeMismatch {
column: "?".into(),
expected: "Integer",
found: other.type_name(),
}),
}
}
pub fn as_bool(&self) -> Result<bool, DbError> {
self.as_i64().map(|i| i != 0)
}
pub fn as_f64(&self) -> Result<f64, DbError> {
match self {
SqlValue::Real(f) => Ok(*f),
SqlValue::Integer(i) => Ok(*i as f64), SqlValue::Null => Err(DbError::NullValue("?".into())),
other => Err(DbError::TypeMismatch {
column: "?".into(),
expected: "Real",
found: other.type_name(),
}),
}
}
pub fn as_text(&self) -> Result<String, DbError> {
match self {
SqlValue::Text(s) => Ok(s.clone()),
SqlValue::Null => Err(DbError::NullValue("?".into())),
other => Err(DbError::TypeMismatch {
column: "?".into(),
expected: "Text",
found: other.type_name(),
}),
}
}
pub fn as_opt_text(&self) -> Result<Option<String>, DbError> {
match self {
SqlValue::Text(s) => Ok(Some(s.clone())),
SqlValue::Null => Ok(None),
other => Err(DbError::TypeMismatch {
column: "?".into(),
expected: "Text or Null",
found: other.type_name(),
}),
}
}
pub fn as_opt_i64(&self) -> Result<Option<i64>, DbError> {
match self {
SqlValue::Integer(i) => Ok(Some(*i)),
SqlValue::Null => Ok(None),
other => Err(DbError::TypeMismatch {
column: "?".into(),
expected: "Integer or Null",
found: other.type_name(),
}),
}
}
}
impl From<i64> for SqlValue {
fn from(v: i64) -> Self {
SqlValue::Integer(v)
}
}
impl From<i32> for SqlValue {
fn from(v: i32) -> Self {
SqlValue::Integer(v as i64)
}
}
impl From<u32> for SqlValue {
fn from(v: u32) -> Self {
SqlValue::Integer(v as i64)
}
}
impl From<bool> for SqlValue {
fn from(v: bool) -> Self {
SqlValue::Integer(v as i64)
}
}
impl From<f64> for SqlValue {
fn from(v: f64) -> Self {
SqlValue::Real(v)
}
}
impl From<f32> for SqlValue {
fn from(v: f32) -> Self {
SqlValue::Real(v as f64)
}
}
impl From<String> for SqlValue {
fn from(v: String) -> Self {
SqlValue::Text(v)
}
}
impl From<&str> for SqlValue {
fn from(v: &str) -> Self {
SqlValue::Text(v.to_string())
}
}
impl From<Option<String>> for SqlValue {
fn from(v: Option<String>) -> Self {
match v {
Some(s) => SqlValue::Text(s),
None => SqlValue::Null,
}
}
}
impl From<Option<i64>> for SqlValue {
fn from(v: Option<i64>) -> Self {
match v {
Some(i) => SqlValue::Integer(i),
None => SqlValue::Null,
}
}
}
impl rusqlite::types::ToSql for SqlValue {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
use rusqlite::types::{ToSqlOutput, Value, ValueRef};
match self {
SqlValue::Null => Ok(ToSqlOutput::Owned(Value::Null)),
SqlValue::Integer(i) => Ok(ToSqlOutput::Owned(Value::Integer(*i))),
SqlValue::Real(f) => Ok(ToSqlOutput::Owned(Value::Real(*f))),
SqlValue::Text(s) => Ok(ToSqlOutput::Borrowed(ValueRef::Text(s.as_bytes()))),
}
}
}
pub(crate) fn from_rusqlite(v: rusqlite::types::Value) -> SqlValue {
use rusqlite::types::Value::*;
match v {
Null => SqlValue::Null,
Integer(i) => SqlValue::Integer(i),
Real(f) => SqlValue::Real(f),
Text(s) => SqlValue::Text(s),
Blob(_) => SqlValue::Null, }
}