#[doc(inline)]
pub use super::wit::pg3::{Error as PgError, *};
use chrono::{Datelike, Timelike};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("error value decoding: {0}")]
Decode(String),
#[error(transparent)]
PgError(#[from] PgError),
}
pub trait Decode: Sized {
fn decode(value: &DbValue) -> Result<Self, Error>;
}
impl<T> Decode for Option<T>
where
T: Decode,
{
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::DbNull => Ok(None),
v => Ok(Some(T::decode(v)?)),
}
}
}
impl Decode for bool {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Boolean(boolean) => Ok(*boolean),
_ => Err(Error::Decode(format_decode_err("BOOL", value))),
}
}
}
impl Decode for i16 {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Int16(n) => Ok(*n),
_ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
}
}
}
impl Decode for i32 {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Int32(n) => Ok(*n),
_ => Err(Error::Decode(format_decode_err("INT", value))),
}
}
}
impl Decode for i64 {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Int64(n) => Ok(*n),
_ => Err(Error::Decode(format_decode_err("BIGINT", value))),
}
}
}
impl Decode for f32 {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Floating32(n) => Ok(*n),
_ => Err(Error::Decode(format_decode_err("REAL", value))),
}
}
}
impl Decode for f64 {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Floating64(n) => Ok(*n),
_ => Err(Error::Decode(format_decode_err("DOUBLE PRECISION", value))),
}
}
}
impl Decode for Vec<u8> {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Binary(n) => Ok(n.to_owned()),
_ => Err(Error::Decode(format_decode_err("BYTEA", value))),
}
}
}
impl Decode for String {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Str(s) => Ok(s.to_owned()),
_ => Err(Error::Decode(format_decode_err(
"CHAR, VARCHAR, TEXT",
value,
))),
}
}
}
impl Decode for chrono::NaiveDate {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Date((year, month, day)) => {
let naive_date =
chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
.ok_or_else(|| {
Error::Decode(format!(
"invalid date y={}, m={}, d={}",
year, month, day
))
})?;
Ok(naive_date)
}
_ => Err(Error::Decode(format_decode_err("DATE", value))),
}
}
}
impl Decode for chrono::NaiveTime {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Time((hour, minute, second, nanosecond)) => {
let naive_time = chrono::NaiveTime::from_hms_nano_opt(
(*hour).into(),
(*minute).into(),
(*second).into(),
*nanosecond,
)
.ok_or_else(|| {
Error::Decode(format!(
"invalid time {}:{}:{}:{}",
hour, minute, second, nanosecond
))
})?;
Ok(naive_time)
}
_ => Err(Error::Decode(format_decode_err("TIME", value))),
}
}
}
impl Decode for chrono::NaiveDateTime {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Datetime((year, month, day, hour, minute, second, nanosecond)) => {
let naive_date =
chrono::NaiveDate::from_ymd_opt(*year, (*month).into(), (*day).into())
.ok_or_else(|| {
Error::Decode(format!(
"invalid date y={}, m={}, d={}",
year, month, day
))
})?;
let naive_time = chrono::NaiveTime::from_hms_nano_opt(
(*hour).into(),
(*minute).into(),
(*second).into(),
*nanosecond,
)
.ok_or_else(|| {
Error::Decode(format!(
"invalid time {}:{}:{}:{}",
hour, minute, second, nanosecond
))
})?;
let dt = chrono::NaiveDateTime::new(naive_date, naive_time);
Ok(dt)
}
_ => Err(Error::Decode(format_decode_err("DATETIME", value))),
}
}
}
impl Decode for chrono::Duration {
fn decode(value: &DbValue) -> Result<Self, Error> {
match value {
DbValue::Timestamp(n) => Ok(chrono::Duration::seconds(*n)),
_ => Err(Error::Decode(format_decode_err("BIGINT", value))),
}
}
}
macro_rules! impl_parameter_value_conversions {
($($ty:ty => $id:ident),*) => {
$(
impl From<$ty> for ParameterValue {
fn from(v: $ty) -> ParameterValue {
ParameterValue::$id(v)
}
}
)*
};
}
impl_parameter_value_conversions! {
i8 => Int8,
i16 => Int16,
i32 => Int32,
i64 => Int64,
f32 => Floating32,
f64 => Floating64,
bool => Boolean,
String => Str,
Vec<u8> => Binary
}
impl From<chrono::NaiveDateTime> for ParameterValue {
fn from(v: chrono::NaiveDateTime) -> ParameterValue {
ParameterValue::Datetime((
v.year(),
v.month() as u8,
v.day() as u8,
v.hour() as u8,
v.minute() as u8,
v.second() as u8,
v.nanosecond(),
))
}
}
impl From<chrono::NaiveTime> for ParameterValue {
fn from(v: chrono::NaiveTime) -> ParameterValue {
ParameterValue::Time((
v.hour() as u8,
v.minute() as u8,
v.second() as u8,
v.nanosecond(),
))
}
}
impl From<chrono::NaiveDate> for ParameterValue {
fn from(v: chrono::NaiveDate) -> ParameterValue {
ParameterValue::Date((v.year(), v.month() as u8, v.day() as u8))
}
}
impl From<chrono::TimeDelta> for ParameterValue {
fn from(v: chrono::TimeDelta) -> ParameterValue {
ParameterValue::Timestamp(v.num_seconds())
}
}
impl<T: Into<ParameterValue>> From<Option<T>> for ParameterValue {
fn from(o: Option<T>) -> ParameterValue {
match o {
Some(v) => v.into(),
None => ParameterValue::DbNull,
}
}
}
fn format_decode_err(types: &str, value: &DbValue) -> String {
format!("Expected {} from the DB but got {:?}", types, value)
}
#[cfg(test)]
mod tests {
use chrono::NaiveDateTime;
use super::*;
#[test]
fn boolean() {
assert!(bool::decode(&DbValue::Boolean(true)).unwrap());
assert!(bool::decode(&DbValue::Int32(0)).is_err());
assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn int16() {
assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
assert!(i16::decode(&DbValue::Int32(0)).is_err());
assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn int32() {
assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
assert!(i32::decode(&DbValue::Boolean(false)).is_err());
assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn int64() {
assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
assert!(i64::decode(&DbValue::Boolean(false)).is_err());
assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn floating32() {
assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
assert!(f32::decode(&DbValue::Boolean(false)).is_err());
assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn floating64() {
assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
assert!(f64::decode(&DbValue::Boolean(false)).is_err());
assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
}
#[test]
fn str() {
assert_eq!(
String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
String::from("foo")
);
assert!(String::decode(&DbValue::Int32(0)).is_err());
assert!(Option::<String>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
#[test]
fn binary() {
assert!(Vec::<u8>::decode(&DbValue::Binary(vec![0, 0])).is_ok());
assert!(Vec::<u8>::decode(&DbValue::Boolean(false)).is_err());
assert!(Option::<Vec<u8>>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
#[test]
fn date() {
assert_eq!(
chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
chrono::NaiveDate::from_ymd_opt(1, 2, 4).unwrap()
);
assert_ne!(
chrono::NaiveDate::decode(&DbValue::Date((1, 2, 4))).unwrap(),
chrono::NaiveDate::from_ymd_opt(1, 2, 5).unwrap()
);
assert!(Option::<chrono::NaiveDate>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
#[test]
fn time() {
assert_eq!(
chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
chrono::NaiveTime::from_hms_nano_opt(1, 2, 3, 4).unwrap()
);
assert_ne!(
chrono::NaiveTime::decode(&DbValue::Time((1, 2, 3, 4))).unwrap(),
chrono::NaiveTime::from_hms_nano_opt(1, 2, 4, 5).unwrap()
);
assert!(Option::<chrono::NaiveTime>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
#[test]
fn datetime() {
let date = chrono::NaiveDate::from_ymd_opt(1, 2, 3).unwrap();
let mut time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 7).unwrap();
assert_eq!(
chrono::NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
chrono::NaiveDateTime::new(date, time)
);
time = chrono::NaiveTime::from_hms_nano_opt(4, 5, 6, 8).unwrap();
assert_ne!(
NaiveDateTime::decode(&DbValue::Datetime((1, 2, 3, 4, 5, 6, 7))).unwrap(),
chrono::NaiveDateTime::new(date, time)
);
assert!(Option::<chrono::NaiveDateTime>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
#[test]
fn timestamp() {
assert_eq!(
chrono::Duration::decode(&DbValue::Timestamp(1)).unwrap(),
chrono::Duration::seconds(1),
);
assert_ne!(
chrono::Duration::decode(&DbValue::Timestamp(2)).unwrap(),
chrono::Duration::seconds(1)
);
assert!(Option::<chrono::Duration>::decode(&DbValue::DbNull)
.unwrap()
.is_none());
}
}