mysql_connector/types/value/
conversion.rs1use {
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 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>);