1use std::io;
16
17use byteorder::{LittleEndian, ReadBytesExt};
18
19use crate::myc::constants::ColumnType;
20use crate::myc::io::ReadMysqlExt;
21
22#[derive(Debug, PartialEq, Copy, Clone)]
24pub struct Value<'a>(ValueInner<'a>);
25
26#[derive(Debug, PartialEq, Copy, Clone)]
28pub enum ValueInner<'a> {
29 NULL,
31 Bytes(&'a [u8]),
33 Int(i64),
35 UInt(u64),
37 Double(f64),
39 Date(&'a [u8]),
42 Time(&'a [u8]),
45 Datetime(&'a [u8]),
49}
50
51impl<'a> Value<'a> {
52 pub fn into_inner(self) -> ValueInner<'a> {
59 self.0
60 }
61
62 pub(crate) fn null() -> Self {
63 Value(ValueInner::NULL)
64 }
65
66 pub fn is_null(&self) -> bool {
68 matches!(self.0, ValueInner::NULL)
69 }
70
71 pub(crate) fn parse_from(
72 input: &mut &'a [u8],
73 ct: ColumnType,
74 unsigned: bool,
75 ) -> io::Result<Self> {
76 ValueInner::parse_from(input, ct, unsigned).map(Value)
77 }
78
79 pub(crate) fn bytes(input: &'a [u8]) -> Value<'a> {
80 Value(ValueInner::Bytes(input))
81 }
82}
83
84macro_rules! read_bytes {
85 ($input:expr, $len:expr) => {
86 if $len as usize > $input.len() {
87 Err(io::Error::new(
88 io::ErrorKind::UnexpectedEof,
89 "EOF while reading length-encoded string",
90 ))
91 } else {
92 let (bits, rest) = $input.split_at($len as usize);
93 *$input = rest;
94 Ok(bits)
95 }
96 };
97}
98
99impl<'a> ValueInner<'a> {
100 fn parse_from(input: &mut &'a [u8], ct: ColumnType, unsigned: bool) -> io::Result<Self> {
101 match ct {
102 ColumnType::MYSQL_TYPE_STRING
103 | ColumnType::MYSQL_TYPE_VAR_STRING
104 | ColumnType::MYSQL_TYPE_BLOB
105 | ColumnType::MYSQL_TYPE_TINY_BLOB
106 | ColumnType::MYSQL_TYPE_MEDIUM_BLOB
107 | ColumnType::MYSQL_TYPE_LONG_BLOB
108 | ColumnType::MYSQL_TYPE_SET
109 | ColumnType::MYSQL_TYPE_ENUM
110 | ColumnType::MYSQL_TYPE_DECIMAL
111 | ColumnType::MYSQL_TYPE_VARCHAR
112 | ColumnType::MYSQL_TYPE_BIT
113 | ColumnType::MYSQL_TYPE_NEWDECIMAL
114 | ColumnType::MYSQL_TYPE_GEOMETRY
115 | ColumnType::MYSQL_TYPE_JSON => {
116 let len = input.read_lenenc_int()?;
117 Ok(ValueInner::Bytes(read_bytes!(input, len)?))
118 }
119 ColumnType::MYSQL_TYPE_TINY => {
120 if unsigned {
121 Ok(ValueInner::UInt(u64::from(input.read_u8()?)))
122 } else {
123 Ok(ValueInner::Int(i64::from(input.read_i8()?)))
124 }
125 }
126 ColumnType::MYSQL_TYPE_SHORT | ColumnType::MYSQL_TYPE_YEAR => {
127 if unsigned {
128 Ok(ValueInner::UInt(u64::from(
129 input.read_u16::<LittleEndian>()?,
130 )))
131 } else {
132 Ok(ValueInner::Int(i64::from(
133 input.read_i16::<LittleEndian>()?,
134 )))
135 }
136 }
137 ColumnType::MYSQL_TYPE_LONG | ColumnType::MYSQL_TYPE_INT24 => {
138 if unsigned {
139 Ok(ValueInner::UInt(u64::from(
140 input.read_u32::<LittleEndian>()?,
141 )))
142 } else {
143 Ok(ValueInner::Int(i64::from(
144 input.read_i32::<LittleEndian>()?,
145 )))
146 }
147 }
148 ColumnType::MYSQL_TYPE_LONGLONG => {
149 if unsigned {
150 Ok(ValueInner::UInt(input.read_u64::<LittleEndian>()?))
151 } else {
152 Ok(ValueInner::Int(input.read_i64::<LittleEndian>()?))
153 }
154 }
155 ColumnType::MYSQL_TYPE_FLOAT => {
156 let f = input.read_f32::<LittleEndian>()?;
157 Ok(ValueInner::Double(f64::from(f)))
158 }
159 ColumnType::MYSQL_TYPE_DOUBLE => {
160 Ok(ValueInner::Double(input.read_f64::<LittleEndian>()?))
161 }
162 ColumnType::MYSQL_TYPE_TIMESTAMP | ColumnType::MYSQL_TYPE_DATETIME => {
163 let len = input.read_u8()?;
164 Ok(ValueInner::Datetime(read_bytes!(input, len)?))
165 }
166 ColumnType::MYSQL_TYPE_DATE => {
167 let len = input.read_u8()?;
168 Ok(ValueInner::Date(read_bytes!(input, len)?))
169 }
170 ColumnType::MYSQL_TYPE_TIME => {
171 let len = input.read_u8()?;
172 Ok(ValueInner::Time(read_bytes!(input, len)?))
173 }
174 ColumnType::MYSQL_TYPE_NULL => Ok(ValueInner::NULL),
175 ct => Err(io::Error::new(
176 io::ErrorKind::InvalidInput,
177 format!("unknown column type {:?}", ct),
178 )),
179 }
180 }
181}
182
183macro_rules! impl_into {
185 ($t:ty, $($variant:path),*) => {
186 impl<'a> From<Value<'a>> for $t {
187 fn from(val: Value<'a>) -> Self {
188 match val.0 {
189 $($variant(v) => v as $t),*,
190 v => panic!(concat!("invalid type conversion from {:?} to ", stringify!($t)), v)
191 }
192 }
193 }
194 }
195}
196
197impl_into!(u8, ValueInner::UInt, ValueInner::Int);
198impl_into!(u16, ValueInner::UInt, ValueInner::Int);
199impl_into!(u32, ValueInner::UInt, ValueInner::Int);
200impl_into!(u64, ValueInner::UInt);
201impl_into!(i8, ValueInner::UInt, ValueInner::Int);
202impl_into!(i16, ValueInner::UInt, ValueInner::Int);
203impl_into!(i32, ValueInner::UInt, ValueInner::Int);
204impl_into!(i64, ValueInner::Int);
205impl_into!(f32, ValueInner::Double);
206impl_into!(f64, ValueInner::Double);
207impl_into!(&'a [u8], ValueInner::Bytes);
208
209impl<'a> From<Value<'a>> for &'a str {
210 fn from(val: Value<'a>) -> Self {
211 if let ValueInner::Bytes(v) = val.0 {
212 ::std::str::from_utf8(v).unwrap()
213 } else {
214 panic!("invalid type conversion from {:?} to string", val)
215 }
216 }
217}
218
219use chrono::{NaiveDate, NaiveDateTime};
220impl<'a> From<Value<'a>> for NaiveDate {
221 fn from(val: Value<'a>) -> Self {
222 if let ValueInner::Date(mut v) = val.0 {
223 assert_eq!(v.len(), 4);
224 NaiveDate::from_ymd_opt(
225 i32::from(v.read_u16::<LittleEndian>().unwrap()),
226 u32::from(v.read_u8().unwrap()),
227 u32::from(v.read_u8().unwrap()),
228 )
229 .unwrap()
230 } else {
231 panic!("invalid type conversion from {:?} to date", val)
232 }
233 }
234}
235
236impl<'a> From<Value<'a>> for NaiveDateTime {
237 fn from(val: Value<'a>) -> Self {
238 to_naive_datetime(val).unwrap()
239 }
240}
241
242pub fn to_naive_datetime(val: Value) -> Result<NaiveDateTime, io::Error> {
243 let ValueInner::Datetime(v) = val.0 else {
244 return Err(io::Error::new(
245 io::ErrorKind::InvalidData,
246 format!("invalid type conversion from {:?} to datetime", val),
247 ));
248 };
249
250 let len = v.len();
251
252 let v = &mut io::Cursor::new(v);
253
254 fn read_ymd(v: &mut io::Cursor<&[u8]>) -> (i32, u32, u32) {
256 let y = i32::from(v.read_u16::<LittleEndian>().unwrap());
257 let m = u32::from(v.read_u8().unwrap());
258 let d = u32::from(v.read_u8().unwrap());
259 (y, m, d)
260 }
261
262 fn read_hms(v: &mut io::Cursor<&[u8]>) -> (u32, u32, u32) {
264 let h = u32::from(v.read_u8().unwrap());
265 let m = u32::from(v.read_u8().unwrap());
266 let s = u32::from(v.read_u8().unwrap());
267 (h, m, s)
268 }
269
270 let d = match len {
273 0 => {
274 return Err(io::Error::new(
275 io::ErrorKind::InvalidData,
276 "'0000-00-00 00:00:00' is a valid timestamp value but not representable by NaiveDateTime!",
277 ))
278 }
279 4 => {
280 let (y, m, d) = read_ymd(v);
281 NaiveDate::from_ymd_opt(y, m, d).and_then(|x| x.and_hms_opt(0, 0, 0))
282 }
283 7 => {
284 let (y, m, d) = read_ymd(v);
285 NaiveDate::from_ymd_opt(y, m, d).and_then(|x| {
286 let (h, m, s) = read_hms(v);
287 x.and_hms_opt(h, m, s)
288 })
289 }
290 11 => {
291 let (y, m, d) = read_ymd(v);
292 NaiveDate::from_ymd_opt(y, m, d).and_then(|x| {
293 let (h, m, s) = read_hms(v);
294
295 let us = v.read_u32::<LittleEndian>().unwrap();
297
298 x.and_hms_micro_opt(h, m, s, us)
299 })
300 }
301 _ => {
302 return Err(io::Error::new(
303 io::ErrorKind::InvalidData,
304 format!("illegal timestamp value length: {}", len),
305 ))
306 }
307 };
308
309 d.ok_or_else(|| {
310 io::Error::new(
311 io::ErrorKind::InvalidData,
312 format!("invalid data conversion from {:?} to datetime", val),
313 )
314 })
315}
316
317use std::time::Duration;
318
319impl<'a> From<Value<'a>> for Duration {
320 fn from(val: Value<'a>) -> Self {
321 if let ValueInner::Time(mut v) = val.0 {
322 assert!(v.is_empty() || v.len() == 8 || v.len() == 12);
323
324 if v.is_empty() {
325 return Duration::from_secs(0);
326 }
327
328 let neg = v.read_u8().unwrap();
329 if neg != 0u8 {
330 unimplemented!();
331 }
332
333 let days = u64::from(v.read_u32::<LittleEndian>().unwrap());
334 let hours = u64::from(v.read_u8().unwrap());
335 let minutes = u64::from(v.read_u8().unwrap());
336 let seconds = u64::from(v.read_u8().unwrap());
337 let micros = if v.len() == 12 {
338 v.read_u32::<LittleEndian>().unwrap()
339 } else {
340 0
341 };
342
343 Duration::new(
344 days * 86_400 + hours * 3_600 + minutes * 60 + seconds,
345 micros * 1_000,
346 )
347 } else {
348 panic!("invalid type conversion from {:?} to datetime", val)
349 }
350 }
351}