spin_sdk/
mysql.rs

1//! Conversions between Rust, WIT and **MySQL** types.
2//!
3//! # Types
4//!
5//! | Rust type | WIT (db-value)      | MySQL type(s)           |
6//! |-----------|---------------------|-------------------------|
7//! | `bool`    | int8(s8)            | TINYINT(1), BOOLEAN     |
8//! | `i8`      | int8(s8)            | TINYINT                 |
9//! | `i16`     | int16(s16)          | SMALLINT                |
10//! | `i32`     | int32(s32)          | MEDIUM, INT             |
11//! | `i64`     | int64(s64)          | BIGINT                  |
12//! | `u8`      | uint8(u8)           | TINYINT UNSIGNED        |
13//! | `u16`     | uint16(u16)         | SMALLINT UNSIGNED       |
14//! | `u32`     | uint32(u32)         | INT UNSIGNED            |
15//! | `u64`     | uint64(u64)         | BIGINT UNSIGNED         |
16//! | `f32`     | floating32(float32) | FLOAT                   |
17//! | `f64`     | floating64(float64) | DOUBLE                  |
18//! | `String`  | str(string)         | VARCHAR, CHAR, TEXT     |
19//! | `Vec<u8>` | binary(list\<u8\>)  | VARBINARY, BINARY, BLOB |
20
21#[doc(inline)]
22pub use super::wit::v2::mysql::{Connection, Error as MysqlError};
23#[doc(inline)]
24pub use super::wit::v2::rdbms_types::*;
25
26/// A MySQL error
27#[derive(Debug, thiserror::Error)]
28pub enum Error {
29    /// Failed to deserialize [`DbValue`]
30    #[error("error value decoding: {0}")]
31    Decode(String),
32    /// MySQL query failed with an error
33    #[error(transparent)]
34    MysqlError(#[from] MysqlError),
35}
36
37/// A type that can be decoded from the database.
38pub trait Decode: Sized {
39    /// Decode a new value of this type using a [`DbValue`].
40    fn decode(value: &DbValue) -> Result<Self, Error>;
41}
42
43impl<T> Decode for Option<T>
44where
45    T: Decode,
46{
47    fn decode(value: &DbValue) -> Result<Self, Error> {
48        match value {
49            DbValue::DbNull => Ok(None),
50            v => Ok(Some(T::decode(v)?)),
51        }
52    }
53}
54
55impl Decode for bool {
56    fn decode(value: &DbValue) -> Result<Self, Error> {
57        match value {
58            DbValue::Int8(0) => Ok(false),
59            DbValue::Int8(1) => Ok(true),
60            _ => Err(Error::Decode(format_decode_err(
61                "TINYINT(1), BOOLEAN",
62                value,
63            ))),
64        }
65    }
66}
67
68impl Decode for i8 {
69    fn decode(value: &DbValue) -> Result<Self, Error> {
70        match value {
71            DbValue::Int8(n) => Ok(*n),
72            _ => Err(Error::Decode(format_decode_err("TINYINT", value))),
73        }
74    }
75}
76
77impl Decode for i16 {
78    fn decode(value: &DbValue) -> Result<Self, Error> {
79        match value {
80            DbValue::Int16(n) => Ok(*n),
81            _ => Err(Error::Decode(format_decode_err("SMALLINT", value))),
82        }
83    }
84}
85
86impl Decode for i32 {
87    fn decode(value: &DbValue) -> Result<Self, Error> {
88        match value {
89            DbValue::Int32(n) => Ok(*n),
90            _ => Err(Error::Decode(format_decode_err("INT", value))),
91        }
92    }
93}
94
95impl Decode for i64 {
96    fn decode(value: &DbValue) -> Result<Self, Error> {
97        match value {
98            DbValue::Int64(n) => Ok(*n),
99            _ => Err(Error::Decode(format_decode_err("BIGINT", value))),
100        }
101    }
102}
103
104impl Decode for u8 {
105    fn decode(value: &DbValue) -> Result<Self, Error> {
106        match value {
107            DbValue::Uint8(n) => Ok(*n),
108            _ => Err(Error::Decode(format_decode_err("UNSIGNED TINYINT", value))),
109        }
110    }
111}
112
113impl Decode for u16 {
114    fn decode(value: &DbValue) -> Result<Self, Error> {
115        match value {
116            DbValue::Uint16(n) => Ok(*n),
117            _ => Err(Error::Decode(format_decode_err("UNSIGNED SMALLINT", value))),
118        }
119    }
120}
121
122impl Decode for u32 {
123    fn decode(value: &DbValue) -> Result<Self, Error> {
124        match value {
125            DbValue::Uint32(n) => Ok(*n),
126            _ => Err(Error::Decode(format_decode_err(
127                "UNISIGNED MEDIUMINT, UNSIGNED INT",
128                value,
129            ))),
130        }
131    }
132}
133
134impl Decode for u64 {
135    fn decode(value: &DbValue) -> Result<Self, Error> {
136        match value {
137            DbValue::Uint64(n) => Ok(*n),
138            _ => Err(Error::Decode(format_decode_err("UNSIGNED BIGINT", value))),
139        }
140    }
141}
142
143impl Decode for f32 {
144    fn decode(value: &DbValue) -> Result<Self, Error> {
145        match value {
146            DbValue::Floating32(n) => Ok(*n),
147            _ => Err(Error::Decode(format_decode_err("FLOAT", value))),
148        }
149    }
150}
151
152impl Decode for f64 {
153    fn decode(value: &DbValue) -> Result<Self, Error> {
154        match value {
155            DbValue::Floating64(n) => Ok(*n),
156            _ => Err(Error::Decode(format_decode_err("DOUBLE", value))),
157        }
158    }
159}
160
161impl Decode for Vec<u8> {
162    fn decode(value: &DbValue) -> Result<Self, Error> {
163        match value {
164            DbValue::Binary(n) => Ok(n.to_owned()),
165            _ => Err(Error::Decode(format_decode_err("BINARY, VARBINARY", value))),
166        }
167    }
168}
169
170impl Decode for String {
171    fn decode(value: &DbValue) -> Result<Self, Error> {
172        match value {
173            DbValue::Str(s) => Ok(s.to_owned()),
174            _ => Err(Error::Decode(format_decode_err(
175                "CHAR, VARCHAR, TEXT",
176                value,
177            ))),
178        }
179    }
180}
181
182fn format_decode_err(types: &str, value: &DbValue) -> String {
183    format!("Expected {} from the DB but got {:?}", types, value)
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn boolean() {
192        assert!(bool::decode(&DbValue::Int8(1)).unwrap());
193        assert!(bool::decode(&DbValue::Int8(3)).is_err());
194        assert!(bool::decode(&DbValue::Int32(0)).is_err());
195        assert!(Option::<bool>::decode(&DbValue::DbNull).unwrap().is_none());
196    }
197
198    #[test]
199    fn int8() {
200        assert_eq!(i8::decode(&DbValue::Int8(0)).unwrap(), 0);
201        assert!(i8::decode(&DbValue::Int32(0)).is_err());
202        assert!(Option::<i8>::decode(&DbValue::DbNull).unwrap().is_none());
203    }
204
205    #[test]
206    fn int16() {
207        assert_eq!(i16::decode(&DbValue::Int16(0)).unwrap(), 0);
208        assert!(i16::decode(&DbValue::Int32(0)).is_err());
209        assert!(Option::<i16>::decode(&DbValue::DbNull).unwrap().is_none());
210    }
211
212    #[test]
213    fn int32() {
214        assert_eq!(i32::decode(&DbValue::Int32(0)).unwrap(), 0);
215        assert!(i32::decode(&DbValue::Boolean(false)).is_err());
216        assert!(Option::<i32>::decode(&DbValue::DbNull).unwrap().is_none());
217    }
218
219    #[test]
220    fn int64() {
221        assert_eq!(i64::decode(&DbValue::Int64(0)).unwrap(), 0);
222        assert!(i64::decode(&DbValue::Boolean(false)).is_err());
223        assert!(Option::<i64>::decode(&DbValue::DbNull).unwrap().is_none());
224    }
225
226    #[test]
227    fn uint8() {
228        assert_eq!(u8::decode(&DbValue::Uint8(0)).unwrap(), 0);
229        assert!(u8::decode(&DbValue::Uint32(0)).is_err());
230        assert!(Option::<u16>::decode(&DbValue::DbNull).unwrap().is_none());
231    }
232
233    #[test]
234    fn uint16() {
235        assert_eq!(u16::decode(&DbValue::Uint16(0)).unwrap(), 0);
236        assert!(u16::decode(&DbValue::Uint32(0)).is_err());
237        assert!(Option::<u16>::decode(&DbValue::DbNull).unwrap().is_none());
238    }
239
240    #[test]
241    fn uint32() {
242        assert_eq!(u32::decode(&DbValue::Uint32(0)).unwrap(), 0);
243        assert!(u32::decode(&DbValue::Boolean(false)).is_err());
244        assert!(Option::<u32>::decode(&DbValue::DbNull).unwrap().is_none());
245    }
246
247    #[test]
248    fn uint64() {
249        assert_eq!(u64::decode(&DbValue::Uint64(0)).unwrap(), 0);
250        assert!(u64::decode(&DbValue::Boolean(false)).is_err());
251        assert!(Option::<u64>::decode(&DbValue::DbNull).unwrap().is_none());
252    }
253
254    #[test]
255    fn floating32() {
256        assert!(f32::decode(&DbValue::Floating32(0.0)).is_ok());
257        assert!(f32::decode(&DbValue::Boolean(false)).is_err());
258        assert!(Option::<f32>::decode(&DbValue::DbNull).unwrap().is_none());
259    }
260
261    #[test]
262    fn floating64() {
263        assert!(f64::decode(&DbValue::Floating64(0.0)).is_ok());
264        assert!(f64::decode(&DbValue::Boolean(false)).is_err());
265        assert!(Option::<f64>::decode(&DbValue::DbNull).unwrap().is_none());
266    }
267
268    #[test]
269    fn str() {
270        assert_eq!(
271            String::decode(&DbValue::Str(String::from("foo"))).unwrap(),
272            String::from("foo")
273        );
274
275        assert!(String::decode(&DbValue::Int32(0)).is_err());
276        assert!(Option::<String>::decode(&DbValue::DbNull)
277            .unwrap()
278            .is_none());
279    }
280}