use std::{result::Result as StdResult, str};
use bytes::Bytes;
use crate::{
Result,
decode::Decode,
error::{DecodeError, Error},
sqlite::{statement::StatementHandle, type_info::SqliteDataType},
};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Value {
Null {
type_info: Option<SqliteDataType>,
},
Integer {
value: i64,
type_info: Option<SqliteDataType>,
},
Double {
value: f64,
type_info: Option<SqliteDataType>,
},
Text {
value: Bytes,
type_info: Option<SqliteDataType>,
},
Blob {
value: Bytes,
type_info: Option<SqliteDataType>,
},
}
impl Value {
pub fn int(&self) -> StdResult<i32, DecodeError> {
match self {
Self::Integer { value, .. } => Ok(i32::try_from(*value)?),
Self::Null { .. } => Err(DecodeError::Conversion("unexpected NULL".into())),
_ => Err(DecodeError::Conversion(
"not an integer or out of range".into(),
)),
}
}
pub fn int64(&self) -> StdResult<i64, DecodeError> {
match self {
Self::Integer { value, .. } => Ok(*value),
Self::Null { .. } => Err(DecodeError::Conversion("unexpected NULL".into())),
_ => Err(DecodeError::Conversion("not an integer".into())),
}
}
pub fn double(&self) -> StdResult<f64, DecodeError> {
match self {
Self::Double { value, .. } => Ok(*value),
Self::Integer { value, .. } => Ok(*value as f64),
Self::Null { .. } => Err(DecodeError::Conversion("unexpected NULL".into())),
_ => Err(DecodeError::Conversion("not a double".into())),
}
}
pub fn blob(&self) -> StdResult<&[u8], DecodeError> {
match self {
Self::Blob { value, .. } => Ok(value.as_ref()),
Self::Text { value, .. } => Ok(value.as_ref()),
Self::Null { .. } => Err(DecodeError::Conversion("unexpected NULL".into())),
_ => Err(DecodeError::Conversion("not blob".into())),
}
}
pub fn text(&self) -> StdResult<&str, DecodeError> {
match self {
Self::Text { value, .. } => str::from_utf8(value.as_ref())
.map_err(|e| DecodeError::Conversion(format!("invalid UTF-8: {e}"))),
Self::Null { .. } => Err(DecodeError::Conversion("unexpected NULL".into())),
_ => Err(DecodeError::Conversion("not text".into())),
}
}
pub fn type_info(&self) -> SqliteDataType {
match self {
Self::Null { type_info } => type_info.unwrap_or(SqliteDataType::Null),
Self::Integer { type_info, .. } => type_info.unwrap_or(SqliteDataType::Int),
Self::Double { type_info, .. } => type_info.unwrap_or(SqliteDataType::Float),
Self::Text { type_info, .. } => type_info.unwrap_or(SqliteDataType::Text),
Self::Blob { type_info, .. } => type_info.unwrap_or(SqliteDataType::Blob),
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null { .. })
}
pub(crate) fn bind(&self, handle: &StatementHandle, i: usize) -> Result<()> {
match self {
Self::Text { value, .. } => {
let text = str::from_utf8(value.as_ref())
.map_err(|e| Error::Decode(DecodeError::Conversion(e.to_string())))?;
handle.bind_text(i, text)?;
}
Self::Blob { value, .. } => handle.bind_blob(i, value.as_ref())?,
Self::Integer { value, .. } => handle.bind_int64(i, *value)?,
Self::Double { value, .. } => handle.bind_double(i, *value)?,
Self::Null { .. } => handle.bind_null(i)?,
}
Ok(())
}
}
impl<'r> Decode<'r> for Value {
fn decode(value: &'r Value) -> StdResult<Self, DecodeError> {
Ok(value.clone())
}
}