From d8af28ddf0e9ca71a8aaa0ca67647c5cc3046c29 Mon Sep 17 00:00:00 2001 From: Diego Reis Date: Thu, 31 Jul 2025 16:04:02 -0300 Subject: [PATCH] Implement FromValue to common Rust's types One step further to help to simplify the API for users. This is in core and not in Rust bind because, in core, this could benefit a broader set of users/developers --- core/error.rs | 6 +++ core/types.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/core/error.rs b/core/error.rs index 97c6d563b..b27f0abe7 100644 --- a/core/error.rs +++ b/core/error.rs @@ -69,6 +69,12 @@ pub enum LimboError { WriteWriteConflict, #[error("No such transaction ID: {0}")] NoSuchTransactionID(String), + #[error("Null value")] + NullValue, + #[error("invalid column type")] + InvalidColumnType, + #[error("Invalid blob size, expected {0}")] + InvalidBlobSize(usize), } #[macro_export] diff --git a/core/types.rs b/core/types.rs index b3f118fb6..6ea53be20 100644 --- a/core/types.rs +++ b/core/types.rs @@ -571,6 +571,141 @@ impl Value { } } +/// Convert a `Value` into the implementors type. +pub trait FromValue: Sealed { + fn from_sql(val: Value) -> Result + where + Self: Sized; +} + +impl FromValue for Value { + fn from_sql(val: Value) -> Result { + Ok(val) + } +} +impl Sealed for crate::Value {} + +impl FromValue for i32 { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Integer(i) => Ok(i as i32), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for i32 {} + +impl FromValue for u32 { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Integer(i) => Ok(i as u32), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for u32 {} + +impl FromValue for i64 { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Integer(i) => Ok(i), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for i64 {} + +impl FromValue for u64 { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Integer(i) => Ok(i as u64), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for u64 {} + +impl FromValue for f64 { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Float(f) => Ok(f), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for f64 {} + +impl FromValue for Vec { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Blob(blob) => Ok(blob), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for Vec {} + +impl FromValue for [u8; N] { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Blob(blob) => blob.try_into().map_err(|_| LimboError::InvalidBlobSize(N)), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for [u8; N] {} + +impl FromValue for String { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Text(s) => Ok(s.to_string()), + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for String {} + +impl FromValue for bool { + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Err(LimboError::NullValue), + Value::Integer(i) => match i { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(LimboError::InvalidColumnType), + }, + _ => unreachable!("invalid value type"), + } + } +} +impl Sealed for bool {} + +impl FromValue for Option +where + T: FromValue, +{ + fn from_sql(val: Value) -> Result { + match val { + Value::Null => Ok(None), + _ => T::from_sql(val).map(Some), + } + } +} +impl Sealed for Option {} + +mod sealed { + pub trait Sealed {} +} +use sealed::Sealed; + #[derive(Debug, Clone, PartialEq)] pub struct SumAggState { pub r_err: f64, // Error term for Kahan-Babushka-Neumaier summation