sqlx_xugu/types/
float.rs

1use crate::arguments::XuguArgumentValue;
2use crate::error::BoxDynError;
3use crate::protocol::text::ColumnType;
4use crate::{Xugu, XuguTypeInfo, XuguValueRef};
5use byteorder::{BigEndian, ByteOrder};
6use sqlx_core::decode::Decode;
7use sqlx_core::encode::{Encode, IsNull};
8use sqlx_core::types::Type;
9use std::borrow::Cow;
10
11fn real_compatible(ty: &XuguTypeInfo) -> bool {
12    // NOTE: `DECIMAL` is explicitly excluded because floating-point numbers have different semantics.
13    matches!(ty.r#type, ColumnType::FLOAT | ColumnType::DOUBLE)
14}
15
16impl Type<Xugu> for f32 {
17    fn type_info() -> XuguTypeInfo {
18        XuguTypeInfo::binary(ColumnType::FLOAT)
19    }
20
21    fn compatible(ty: &XuguTypeInfo) -> bool {
22        real_compatible(ty)
23    }
24}
25
26impl Type<Xugu> for f64 {
27    fn type_info() -> XuguTypeInfo {
28        XuguTypeInfo::binary(ColumnType::DOUBLE)
29    }
30
31    fn compatible(ty: &XuguTypeInfo) -> bool {
32        real_compatible(ty)
33    }
34}
35
36impl Encode<'_, Xugu> for f32 {
37    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
38        let buf = self.to_be_bytes().to_vec();
39        args.push(XuguArgumentValue::Bin(Cow::Owned(buf)));
40
41        Ok(IsNull::No)
42    }
43}
44
45impl Encode<'_, Xugu> for f64 {
46    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue<'_>>) -> Result<IsNull, BoxDynError> {
47        let buf = self.to_be_bytes().to_vec();
48        args.push(XuguArgumentValue::Bin(Cow::Owned(buf)));
49
50        Ok(IsNull::No)
51    }
52}
53
54impl Decode<'_, Xugu> for f32 {
55    fn decode(value: XuguValueRef<'_>) -> Result<Self, BoxDynError> {
56        let buf = value.as_bytes()?;
57
58        Ok(match buf.len() {
59            // These functions panic if `buf` is not exactly the right size.
60            4 => BigEndian::read_f32(buf),
61            // Xugu can return 8-byte DOUBLE values for a FLOAT
62            // We take and truncate to f32 as that's the same behavior as *in* Xugu,
63            #[allow(clippy::cast_possible_truncation)]
64            8 => BigEndian::read_f64(buf) as f32,
65            other => {
66                // Users may try to decode a DECIMAL as floating point;
67                // inform them why that's a bad idea.
68                return Err(format!(
69                    "expected a FLOAT as 4 or 8 bytes, got {other} bytes; \
70                             note that decoding DECIMAL as `f32` is not supported \
71                             due to differing semantics"
72                )
73                .into());
74            }
75        })
76    }
77}
78
79impl Decode<'_, Xugu> for f64 {
80    fn decode(value: XuguValueRef<'_>) -> Result<Self, BoxDynError> {
81        let buf = value.as_bytes()?;
82
83        // The `read_*` functions panic if `buf` is not exactly the right size.
84        Ok(match buf.len() {
85            // Allow implicit widening here
86            4 => BigEndian::read_f32(buf) as f64,
87            8 => BigEndian::read_f64(buf),
88            other => {
89                // Users may try to decode a DECIMAL as floating point;
90                // inform them why that's a bad idea.
91                return Err(format!(
92                    "expected a DOUBLE as 4 or 8 bytes, got {other} bytes; \
93                             note that decoding DECIMAL as `f64` is not supported \
94                             due to differing semantics"
95                )
96                .into());
97            }
98        })
99    }
100}