spin_sdk/
pg.rs

1//! Conversions between Rust, WIT and **Postgres** types.
2//!
3//! # Types
4//!
5//! | Rust type  | WIT (db-value)      | Postgres type(s)             |
6//! |------------|---------------------|----------------------------- |
7//! | `bool`     | boolean(bool)       | BOOL                         |
8//! | `i16`      | int16(s16)          | SMALLINT, SMALLSERIAL, INT2  |
9//! | `i32`      | int32(s32)          | INT, SERIAL, INT4            |
10//! | `i64`      | int64(s64)          | BIGINT, BIGSERIAL, INT8      |
11//! | `f32`      | floating32(float32) | REAL, FLOAT4                 |
12//! | `f64`      | floating64(float64) | DOUBLE PRECISION, FLOAT8     |
13//! | `String`   | str(string)         | VARCHAR, CHAR(N), TEXT       |
14//! | `Vec<u8>`  | binary(list\<u8\>)  | BYTEA                        |
15
16#[doc(inline)]
17pub use super::wit::v2::postgres::{Connection, Error as PgError};
18#[doc(inline)]
19pub use super::wit::v2::rdbms_types::*;
20
21/// A pg error
22#[derive(Debug, thiserror::Error)]
23pub enum Error {
24    /// Failed to deserialize [`DbValue`]
25    #[error("error value decoding: {0}")]
26    Decode(String),
27    /// Pg query failed with an error
28    #[error(transparent)]
29    PgError(#[from] PgError),
30}
31
32/// A type that can be decoded from the database.
33pub trait Decode: Sized {
34    /// Decode a new value of this type using a [`DbValue`].
35    fn decode(value: &DbValue) -> Result<Self, Error>;
36}
37
38impl<T> Decode for Option<T>
39where
40    T: Decode,
41{
42    fn decode(value: &DbValue) -> Result<Self, Error> {
43        match value {
44            DbValue::DbNull => Ok(None),
45            v => Ok(Some(T::decode(v)?)),
46        }
47    }
48}
49
50impl Decode for bool {
51    fn decode(value: &DbValue) -> Result<Self, Error> {
52        match value {
53            DbValue::Boolean(boolean) => Ok(*boolean),
54            _ => Err(Error::Decode(format_decode_err("BOOL", value))),
55        }
56    }
57}
58
59impl Decode for i16 {
60    fn decode(value: &DbValue) -> Result<Self, Error> {
61        match value {
62            DbValue::Int16(n) => Ok(*n),
63            _ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
64        }
65    }
66}
67
68impl Decode for i32 {
69    fn decode(value: &DbValue) -> Result<Self, Error> {
70        match value {
71            DbValue::Int32(n) => Ok(*n),
72            _ => Err(Error::Decode(format_decode_err("INT", value))),
73        }
74    }
75}
76
77impl Decode for i64 {
78    fn decode(value: &DbValue) -> Result<Self, Error> {
79        match value {
80            DbValue::Int64(n) => Ok(*n),
81            _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
82        }
83    }
84}
85
86impl Decode for f32 {
87    fn decode(value: &DbValue) -> Result<Self, Error> {
88        match value {
89            DbValue::Floating32(n) => Ok(*n),
90            _ => Err(Error::Decode(format_decode_err("REAL", value))),
91        }
92    }
93}
94
95impl Decode for f64 {
96    fn decode(value: &DbValue) -> Result<Self, Error> {
97        match value {
98            DbValue::Floating64(n) => Ok(*n),
99            _ => Err(Error::Decode(format_decode_err("DOUBLE PRECISION", value))),
100        }
101    }
102}
103
104impl Decode for Vec<u8> {
105    fn decode(value: &DbValue) -> Result<Self, Error> {
106        match value {
107            DbValue::Binary(n) => Ok(n.to_owned()),
108            _ => Err(Error::Decode(format_decode_err("BYTEA", value))),
109        }
110    }
111}
112
113impl Decode for String {
114    fn decode(value: &DbValue) -> Result<Self, Error> {
115        match value {
116            DbValue::Str(s) => Ok(s.to_owned()),
117            _ => Err(Error::Decode(format_decode_err(
118                "CHAR, VARCHAR, TEXT",
119                value,
120            ))),
121        }
122    }
123}
124
125fn format_decode_err(types: &str, value: &DbValue) -> String {
126    format!("Expected {} from the DB but got {:?}", types, value)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn boolean() {
135        assert!(bool::decode(&DbValue::Boolean(true)).unwrap());
136        assert!(bool::decode(&DbValue::Int32(0)).is_err());
137        assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
138    }
139
140    #[test]
141    fn int16() {
142        assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
143        assert!(i16::decode(&DbValue::Int32(0)).is_err());
144        assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
145    }
146
147    #[test]
148    fn int32() {
149        assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
150        assert!(i32::decode(&DbValue::Boolean(false)).is_err());
151        assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
152    }
153
154    #[test]
155    fn int64() {
156        assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
157        assert!(i64::decode(&DbValue::Boolean(false)).is_err());
158        assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
159    }
160
161    #[test]
162    fn floating32() {
163        assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
164        assert!(f32::decode(&DbValue::Boolean(false)).is_err());
165        assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
166    }
167
168    #[test]
169    fn floating64() {
170        assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
171        assert!(f64::decode(&DbValue::Boolean(false)).is_err());
172        assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
173    }
174
175    #[test]
176    fn str() {
177        assert_eq!(
178            String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
179            String::from("foo")
180        );
181
182        assert!(String::decode(&DbValue::Int32(0)).is_err());
183        assert!(Option::<String>::decode(&DbValue::DbNull)
184            .unwrap()
185            .is_none());
186    }
187
188    #[test]
189    fn binary() {
190        assert!(Vec::<u8>::decode(&DbValue::Binary(vec![0, 0])).is_ok());
191        assert!(Vec::<u8>::decode(&DbValue::Boolean(false)).is_err());
192        assert!(Option::<Vec<u8>>::decode(&DbValue::DbNull)
193            .unwrap()
194            .is_none());
195    }
196}