tank_postgres/
value_wrap.rs

1use crate::util::extract_value;
2use bytes::BytesMut;
3use postgres_types::{FromSql, IsNull, ToSql, Type, to_sql_checked};
4use rust_decimal::{Decimal, prelude::FromPrimitive};
5use std::error::Error;
6use tank_core::Value;
7
8#[derive(Debug, Default, Clone)]
9pub(crate) struct ValueWrap(pub(crate) Value);
10
11impl From<Value> for ValueWrap {
12    fn from(value: Value) -> Self {
13        ValueWrap(value)
14    }
15}
16
17impl<'a> FromSql<'a> for ValueWrap {
18    fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
19        Self::from_sql_nullable(ty, Some(raw))
20    }
21    fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
22        Self::from_sql_nullable(ty, None)
23    }
24    fn from_sql_nullable(
25        ty: &Type,
26        raw: Option<&'a [u8]>,
27    ) -> Result<Self, Box<dyn Error + Sync + Send>> {
28        extract_value(ty, raw).map(Into::into)
29    }
30
31    fn accepts(_ty: &Type) -> bool {
32        true
33    }
34}
35
36impl ToSql for ValueWrap {
37    fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>>
38    where
39        Self: Sized,
40    {
41        match &self.0 {
42            Value::Null => None::<String>.to_sql(ty, out),
43            Value::Boolean(v) => v.to_sql(ty, out),
44            Value::Int8(v) => v.to_sql(ty, out),
45            Value::Int16(v) => v.to_sql(ty, out),
46            Value::Int32(v) => v.to_sql(ty, out),
47            Value::Int64(v) => v.to_sql(ty, out),
48            Value::Int128(v) => v.map(|v| Decimal::from_i128(v)).to_sql(ty, out),
49            Value::UInt8(v) => v.map(|v| v as i16).to_sql(ty, out),
50            Value::UInt16(v) => v.map(|v| v as i32).to_sql(ty, out),
51            Value::UInt32(v) => v.to_sql(ty, out),
52            Value::UInt64(v) => v.map(|v| Decimal::from_u64(v)).to_sql(ty, out),
53            Value::UInt128(v) => v.map(|v| Decimal::from_u128(v)).to_sql(ty, out),
54            Value::Float32(v) => v.to_sql(ty, out),
55            Value::Float64(v) => v.to_sql(ty, out),
56            Value::Decimal(v, _, _) => v.to_sql(ty, out),
57            Value::Char(v) => v.map(|v| v.to_string()).to_sql(ty, out),
58            Value::Varchar(v) => v.to_sql(ty, out),
59            Value::Blob(v) => v.as_deref().to_sql(ty, out),
60            Value::Date(v) => v.to_sql(ty, out),
61            Value::Time(v) => v.to_sql(ty, out),
62            Value::Timestamp(v) => v.to_sql(ty, out),
63            Value::TimestampWithTimezone(v) => v.to_sql(ty, out),
64            Value::Uuid(v) => v.to_sql(ty, out),
65            Value::Array(v, ..) => v
66                .as_ref()
67                .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
68                .to_sql(ty, out),
69            Value::List(v, ..) => v
70                .as_ref()
71                .map(|v| v.clone().into_iter().map(ValueWrap).collect::<Vec<_>>())
72                .to_sql(ty, out),
73            _ => {
74                return Err(tank_core::Error::msg(format!(
75                    "tank::Value variant `{:?}` is not supported by Postgres",
76                    &self.0
77                ))
78                .into());
79            }
80        }
81    }
82
83    fn accepts(_ty: &Type) -> bool
84    where
85        Self: Sized,
86    {
87        true
88    }
89
90    to_sql_checked!();
91}
92pub fn postgres_type_to_value(ty: &Type) -> Value {
93    match *ty {
94        Type::BOOL => Value::Boolean(None),
95        Type::CHAR => Value::Int8(None),
96        Type::INT2 => Value::Int16(None),
97        Type::INT4 => Value::Int32(None),
98        Type::INT8 => Value::Int64(None),
99        Type::FLOAT4 => Value::Float32(None),
100        Type::FLOAT8 => Value::Float64(None),
101        Type::NUMERIC => Value::Decimal(None, 0, 0),
102        Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::JSON | Type::XML => Value::Varchar(None),
103        Type::BYTEA => Value::Blob(None),
104        Type::DATE => Value::Date(None),
105        Type::TIME => Value::Time(None),
106        Type::TIMESTAMP => Value::Timestamp(None),
107        Type::TIMESTAMPTZ => Value::TimestampWithTimezone(None),
108        Type::INTERVAL => Value::Interval(None),
109        Type::UUID => Value::Uuid(None),
110        Type::BOOL_ARRAY => Value::List(None, Box::new(Value::Boolean(None))),
111        Type::INT2_ARRAY => Value::List(None, Box::new(Value::Int16(None))),
112        Type::INT4_ARRAY => Value::List(None, Box::new(Value::Int32(None))),
113        Type::INT8_ARRAY => Value::List(None, Box::new(Value::Int64(None))),
114        Type::FLOAT4_ARRAY => Value::List(None, Box::new(Value::Float32(None))),
115        Type::FLOAT8_ARRAY => Value::List(None, Box::new(Value::Float64(None))),
116        Type::NUMERIC_ARRAY => Value::List(None, Box::new(Value::Decimal(None, 0, 0))),
117        Type::TEXT_ARRAY | Type::VARCHAR_ARRAY => Value::List(None, Box::new(Value::Varchar(None))),
118        Type::BYTEA_ARRAY => Value::List(None, Box::new(Value::Blob(None))),
119        Type::DATE_ARRAY => Value::List(None, Box::new(Value::Date(None))),
120        Type::TIME_ARRAY => Value::List(None, Box::new(Value::Time(None))),
121        Type::TIMESTAMP_ARRAY => Value::List(None, Box::new(Value::Timestamp(None))),
122        Type::TIMESTAMPTZ_ARRAY => Value::List(None, Box::new(Value::TimestampWithTimezone(None))),
123        Type::INTERVAL_ARRAY => Value::List(None, Box::new(Value::Interval(None))),
124        Type::UUID_ARRAY => Value::List(None, Box::new(Value::Uuid(None))),
125        _ => Value::Null,
126    }
127}
128
129pub(crate) struct VecWrap<T>(pub Vec<T>);
130
131impl<'a, T: FromSql<'a>> FromSql<'a> for VecWrap<T> {
132    fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
133        Vec::<T>::from_sql_null(ty).map(VecWrap)
134    }
135    fn from_sql_nullable(
136        ty: &Type,
137        raw: Option<&'a [u8]>,
138    ) -> Result<Self, Box<dyn Error + Sync + Send>> {
139        Vec::<T>::from_sql_nullable(ty, raw).map(VecWrap)
140    }
141    fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
142        Vec::<T>::from_sql(ty, raw).map(VecWrap)
143    }
144    fn accepts(ty: &Type) -> bool {
145        Vec::<T>::accepts(ty)
146    }
147}
148
149impl From<VecWrap<ValueWrap>> for Vec<Value> {
150    fn from(value: VecWrap<ValueWrap>) -> Self {
151        value.0.into_iter().map(|v| v.0).collect()
152    }
153}