sqlx_xugu/types/
std_duration.rs

1use crate::arguments::XuguArgumentValue;
2use crate::protocol::text::ColumnType;
3use crate::{Xugu, XuguTypeInfo, XuguValueRef};
4use bytes::Buf;
5use sqlx_core::decode::Decode;
6use sqlx_core::encode::{Encode, IsNull};
7use sqlx_core::error::BoxDynError;
8use sqlx_core::types::Type;
9use std::borrow::Cow;
10use std::time::Duration;
11
12/// The number of seconds per in days.
13const SECONDS_PER_DAY_F: f64 = 86400.0;
14
15impl Type<Xugu> for Duration {
16    fn type_info() -> XuguTypeInfo {
17        XuguTypeInfo::binary(ColumnType::INTERVAL_D2S)
18    }
19
20    fn compatible(ty: &XuguTypeInfo) -> bool {
21        matches!(
22            ty.r#type,
23            ColumnType::INTERVAL_D
24                | ColumnType::INTERVAL_D2H
25                | ColumnType::INTERVAL_D2M
26                | ColumnType::INTERVAL_D2S
27                | ColumnType::INTERVAL_H
28                | ColumnType::INTERVAL_H2M
29                | ColumnType::INTERVAL_H2S
30                | ColumnType::INTERVAL_MI
31                | ColumnType::INTERVAL_M2S
32                | ColumnType::INTERVAL_S
33                | ColumnType::NUMERIC
34        )
35    }
36}
37
38impl Encode<'_, Xugu> for Duration {
39    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue>) -> Result<IsNull, BoxDynError> {
40        let us: i64 = self.as_micros().try_into().map_err(|_| {
41            format!("value {self:?} would overflow binary encoding for Xugu INTERVAL DAY TO SECOND")
42        })?;
43
44        let buf = us.to_be_bytes().to_vec();
45        args.push(XuguArgumentValue::Bin(Cow::Owned(buf)));
46
47        Ok(IsNull::No)
48    }
49}
50
51/// [`std::time::Duration`] 表示的持续时间为非负数,解码时取绝对值
52impl<'r> Decode<'r, Xugu> for Duration {
53    fn decode(value: XuguValueRef<'r>) -> Result<Self, BoxDynError> {
54        let ty = value.type_info.r#type;
55        // 数值型,单位:天
56        if ty == ColumnType::NUMERIC {
57            let d_str = value.as_str()?;
58            let days: f64 = d_str.parse()?;
59            let secs = days.abs() * SECONDS_PER_DAY_F;
60            let delta = Duration::from_secs_f64(secs);
61
62            return Ok(delta);
63        }
64
65        let mut buf = value.as_bytes()?;
66
67        match ty {
68            // 精确到天
69            ColumnType::INTERVAL_D => {
70                let day: i32 = buf.get_i32();
71                let days = day.abs() as f64;
72                let secs = days * SECONDS_PER_DAY_F;
73                let delta = Duration::from_secs_f64(secs);
74                Ok(delta)
75            }
76            // 精确到小时
77            ColumnType::INTERVAL_D2H | ColumnType::INTERVAL_H => {
78                let h: i32 = buf.get_i32();
79                let delta = Duration::from_hours(h.abs() as u64);
80                Ok(delta)
81            }
82            // 精确到分钟
83            ColumnType::INTERVAL_D2M | ColumnType::INTERVAL_H2M | ColumnType::INTERVAL_MI => {
84                let min: i32 = buf.get_i32();
85                let delta = Duration::from_mins(min.abs() as u64);
86                Ok(delta)
87            }
88            // 精确到秒
89            ColumnType::INTERVAL_D2S
90            | ColumnType::INTERVAL_H2S
91            | ColumnType::INTERVAL_M2S
92            | ColumnType::INTERVAL_S => {
93                let us: i64 = buf.get_i64();
94                let delta = Duration::from_micros(us.abs() as u64);
95                Ok(delta)
96            }
97            _ => Err(BoxDynError::from(
98                "[E50044] Resultset: Required type conversion not allowed",
99            )),
100        }
101    }
102}