#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::Text(s.to_owned())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::Text(s)
}
}
impl From<i32> for Value {
fn from(n: i32) -> Self {
Value::Integer(i64::from(n))
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Integer(n)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Value::Real(f)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Integer(if b { 1 } else { 0 })
}
}
impl From<Vec<u8>> for Value {
fn from(b: Vec<u8>) -> Self {
Value::Blob(b)
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(opt: Option<T>) -> Self {
match opt {
Some(v) => v.into(),
None => Value::Null,
}
}
}
#[cfg(feature = "libsql")]
impl From<Value> for libsql::Value {
fn from(v: Value) -> Self {
match v {
Value::Null => libsql::Value::Null,
Value::Integer(n) => libsql::Value::Integer(n),
Value::Real(f) => libsql::Value::Real(f),
Value::Text(s) => libsql::Value::Text(s),
Value::Blob(b) => libsql::Value::Blob(b),
}
}
}
#[cfg(feature = "libsql")]
impl From<libsql::Value> for Value {
fn from(v: libsql::Value) -> Self {
match v {
libsql::Value::Null => Value::Null,
libsql::Value::Integer(n) => Value::Integer(n),
libsql::Value::Real(f) => Value::Real(f),
libsql::Value::Text(s) => Value::Text(s),
libsql::Value::Blob(b) => Value::Blob(b),
}
}
}
#[cfg(feature = "rusqlite")]
impl rusqlite::types::ToSql for Value {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
match self {
Value::Null => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Null,
)),
Value::Integer(n) => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Integer(*n),
)),
Value::Real(f) => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Real(*f),
)),
Value::Text(s) => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Text(s.clone()),
)),
Value::Blob(b) => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Blob(b.clone()),
)),
}
}
}
#[cfg(feature = "postgres")]
mod pg_conversions {
use postgres_types::ToSql;
use super::Value;
#[derive(Debug)]
struct PgNull;
impl postgres_types::ToSql for PgNull {
fn to_sql(
&self,
_ty: &postgres_types::Type,
_out: &mut bytes::BytesMut,
) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Send + Sync>> {
Ok(postgres_types::IsNull::Yes)
}
fn accepts(_ty: &postgres_types::Type) -> bool {
true
}
postgres_types::to_sql_checked!();
}
#[derive(Debug)]
struct FlexibleText(String);
impl postgres_types::ToSql for FlexibleText {
fn to_sql(
&self,
ty: &postgres_types::Type,
out: &mut bytes::BytesMut,
) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Send + Sync>> {
if *ty == postgres_types::Type::UUID {
let uuid: uuid::Uuid = self.0.parse()?;
uuid.to_sql(ty, out)
} else {
self.0.to_sql(ty, out)
}
}
fn accepts(ty: &postgres_types::Type) -> bool {
*ty == postgres_types::Type::UUID || <String as postgres_types::ToSql>::accepts(ty)
}
postgres_types::to_sql_checked!();
}
pub fn to_pg_params(params: &[Value]) -> Vec<Box<dyn ToSql + Sync + Send>> {
params
.iter()
.map(|v| -> Box<dyn ToSql + Sync + Send> {
match v {
Value::Null => Box::new(PgNull),
Value::Integer(n) => Box::new(*n),
Value::Real(f) => Box::new(*f),
Value::Text(s) => Box::new(FlexibleText(s.clone())),
Value::Blob(b) => Box::new(b.clone()),
}
})
.collect()
}
}
#[cfg(feature = "postgres")]
pub use pg_conversions::to_pg_params;