mysql_connector/types/value/
conversion.rs

1use {
2    super::{Value, ValueType},
3    crate::error::{ParseError, SerializeError},
4    chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike},
5};
6
7macro_rules! impl_conversion {
8    ($t:ty, $name:ident) => {
9        impl From<$t> for Value {
10            fn from(value: $t) -> Self {
11                Value::$name(value)
12            }
13        }
14
15        impl TryInto<$t> for Value {
16            type Error = ParseError;
17
18            fn try_into(self) -> std::result::Result<$t, Self::Error> {
19                match self {
20                    Value::$name(x) => Ok(x),
21                    _ => Err(Self::Error::wrong_value(ValueType::$name, self)),
22                }
23            }
24        }
25    };
26}
27
28impl_conversion!(i8, Tiny);
29impl_conversion!(i16, Short);
30impl_conversion!(i32, Int);
31impl_conversion!(i64, Long);
32
33impl_conversion!(u8, UTiny);
34impl_conversion!(u16, UShort);
35impl_conversion!(u32, UInt);
36impl_conversion!(u64, ULong);
37
38impl_conversion!(f32, Float);
39impl_conversion!(f64, Double);
40
41impl From<bool> for Value {
42    fn from(value: bool) -> Self {
43        Value::Tiny(if value { 1 } else { 0 })
44    }
45}
46
47impl TryInto<bool> for Value {
48    type Error = ParseError;
49
50    fn try_into(self) -> Result<bool, Self::Error> {
51        match self {
52            Value::Tiny(x) => Ok(x > 0),
53            Value::Short(x) => Ok(x > 0),
54            Value::Int(x) => Ok(x > 0),
55            Value::Long(x) => Ok(x > 0),
56            Value::UTiny(x) => Ok(x > 0),
57            Value::UShort(x) => Ok(x > 0),
58            Value::UInt(x) => Ok(x > 0),
59            Value::ULong(x) => Ok(x > 0),
60            _ => Err(Self::Error::wrong_value(ValueType::Number, self.clone())),
61        }
62    }
63}
64
65impl From<String> for Value {
66    fn from(value: String) -> Self {
67        Value::Bytes(value.into_bytes())
68    }
69}
70
71impl TryInto<String> for Value {
72    type Error = ParseError;
73
74    fn try_into(self) -> Result<String, Self::Error> {
75        match self {
76            Value::Bytes(x) => String::from_utf8(x).map_err(Into::into),
77            _ => Err(Self::Error::wrong_value(ValueType::Bytes, self)),
78        }
79    }
80}
81
82impl From<Vec<u8>> for Value {
83    fn from(value: Vec<u8>) -> Self {
84        Value::Bytes(value)
85    }
86}
87
88impl TryInto<Vec<u8>> for Value {
89    type Error = ParseError;
90
91    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
92        match self {
93            Value::Bytes(x) => Ok(x),
94            _ => Err(Self::Error::wrong_value(ValueType::Bytes, self)),
95        }
96    }
97}
98
99impl TryFrom<NaiveDate> for Value {
100    type Error = SerializeError;
101
102    fn try_from(value: NaiveDate) -> Result<Self, Self::Error> {
103        Ok(Value::Date(
104            value
105                .year()
106                .try_into()
107                .map_err(|_| SerializeError::InvalidValue(ValueType::Date, Box::new(value)))?,
108            value.month() as u8,
109            value.day() as u8,
110        ))
111    }
112}
113
114impl TryFrom<NaiveDateTime> for Value {
115    type Error = SerializeError;
116
117    fn try_from(value: NaiveDateTime) -> Result<Self, Self::Error> {
118        Ok(Value::Datetime(
119            value
120                .year()
121                .try_into()
122                .map_err(|_| SerializeError::InvalidValue(ValueType::Datetime, Box::new(value)))?,
123            value.month() as u8,
124            value.day() as u8,
125            value.hour() as u8,
126            value.minute() as u8,
127            value.second() as u8,
128            value.nanosecond() / 1_000,
129        ))
130    }
131}
132
133impl TryInto<NaiveDate> for Value {
134    type Error = ParseError;
135
136    fn try_into(self) -> Result<NaiveDate, Self::Error> {
137        match self {
138            Value::Date(year, month, day) | Value::Datetime(year, month, day, _, _, _, _) => {
139                NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32)
140                    .ok_or(ParseError::ValueOutOfBounds(self.clone()))
141            }
142            _ => Err(Self::Error::wrong_value(ValueType::Date, self)),
143        }
144    }
145}
146
147impl TryInto<NaiveDateTime> for Value {
148    type Error = ParseError;
149
150    fn try_into(self) -> Result<NaiveDateTime, Self::Error> {
151        match self {
152            Value::Datetime(year, month, day, hour, minute, second, micro) => {
153                let date = match NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32) {
154                    Some(x) => x,
155                    None => return Err(ParseError::ValueOutOfBounds(self)),
156                };
157                let time =
158                    NaiveTime::from_hms_micro_opt(hour as u32, minute as u32, second as u32, micro)
159                        .ok_or(ParseError::ValueOutOfBounds(self))?;
160                Ok(NaiveDateTime::new(date, time))
161            }
162            _ => Err(Self::Error::wrong_value(ValueType::Datetime, self)),
163        }
164    }
165}
166
167impl From<Duration> for Value {
168    fn from(value: Duration) -> Self {
169        let seconds = value.num_seconds().abs();
170        let minutes = seconds / 60;
171        let hours = minutes / 60;
172        let days = hours / 24;
173
174        Value::Time(
175            value.num_seconds() < 0,
176            days as u32,
177            (hours % 24) as u8,
178            (minutes % 60) as u8,
179            (seconds % 60) as u8,
180            value.subsec_nanos().unsigned_abs() / 1_000,
181        )
182    }
183}
184
185impl TryInto<Duration> for Value {
186    type Error = ParseError;
187
188    fn try_into(self) -> Result<Duration, Self::Error> {
189        match self {
190            Value::Time(negative, days, hours, minutes, seconds, micros) => {
191                const MICROS_PER_SEC: u32 = 1_000_000;
192                if micros >= MICROS_PER_SEC {
193                    return Err(ParseError::ValueOutOfBounds(self.clone()));
194                }
195                let mut x = days as i64;
196                x *= 24;
197                x += hours as i64;
198                x *= 60;
199                x += minutes as i64;
200                x *= 60;
201                x += seconds as i64;
202
203                // Duration is saved as seconds plus positive shift in nanoseconds.
204                // This means, -5.2 seconds becomes -6 seconds plus 800_000_000 nanoseconds.
205                let nanos = if negative {
206                    x *= -1;
207
208                    if micros != 0 {
209                        x -= 1;
210                        (MICROS_PER_SEC - micros) * 1_000
211                    } else {
212                        0
213                    }
214                } else {
215                    micros * 1_000
216                };
217
218                Duration::new(x, nanos).ok_or_else(|| ParseError::ValueOutOfBounds(self.clone()))
219            }
220            _ => Err(Self::Error::wrong_value(ValueType::Time, self.clone())),
221        }
222    }
223}
224
225impl<T> TryFrom<Option<T>> for Value
226where
227    T: TryInto<Value>,
228{
229    type Error = <T as TryInto<Value>>::Error;
230
231    fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
232        match value {
233            Some(value) => value.try_into(),
234            None => Ok(Self::Null),
235        }
236    }
237}
238
239macro_rules! impl_try_into_option {
240    ($($t:ty),* $(,)?) => {
241        $(
242            impl TryInto<Option<$t>> for $crate::types::Value {
243                type Error = $crate::error::ParseError;
244
245                fn try_into(self) -> Result<Option<$t>, Self::Error> {
246                    match self {
247                        $crate::types::Value::Null => Ok(None),
248                        x => x.try_into().map(Some),
249                    }
250                }
251            }
252        )*
253    };
254}
255pub(crate) use impl_try_into_option;
256
257impl_try_into_option!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool);
258impl_try_into_option!(String, NaiveDate, NaiveDateTime, Duration, Vec<u8>);