sqlx_postgres/types/geometry/
line.rs

1use crate::decode::Decode;
2use crate::encode::{Encode, IsNull};
3use crate::error::BoxDynError;
4use crate::types::Type;
5use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
6use sqlx_core::bytes::Buf;
7use std::str::FromStr;
8
9const ERROR: &str = "error decoding LINE";
10
11/// ## Postgres Geometric Line type
12///
13/// Description: Infinite line
14/// Representation: `{A, B, C}`
15///
16/// Lines are represented by the linear equation Ax + By + C = 0, where A and B are not both zero.
17///
18/// See https://www.postgresql.org/docs/16/datatype-geometric.html#DATATYPE-LINE
19#[derive(Debug, Clone, PartialEq)]
20pub struct PgLine {
21    pub a: f64,
22    pub b: f64,
23    pub c: f64,
24}
25
26impl Type<Postgres> for PgLine {
27    fn type_info() -> PgTypeInfo {
28        PgTypeInfo::with_name("line")
29    }
30}
31
32impl PgHasArrayType for PgLine {
33    fn array_type_info() -> PgTypeInfo {
34        PgTypeInfo::with_name("_line")
35    }
36}
37
38impl<'r> Decode<'r, Postgres> for PgLine {
39    fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
40        match value.format() {
41            PgValueFormat::Text => Ok(PgLine::from_str(value.as_str()?)?),
42            PgValueFormat::Binary => Ok(PgLine::from_bytes(value.as_bytes()?)?),
43        }
44    }
45}
46
47impl<'q> Encode<'q, Postgres> for PgLine {
48    fn produces(&self) -> Option<PgTypeInfo> {
49        Some(PgTypeInfo::with_name("line"))
50    }
51
52    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
53        self.serialize(buf)?;
54        Ok(IsNull::No)
55    }
56}
57
58impl FromStr for PgLine {
59    type Err = BoxDynError;
60
61    fn from_str(s: &str) -> Result<Self, Self::Err> {
62        let mut parts = s
63            .trim_matches(|c| c == '{' || c == '}' || c == ' ')
64            .split(',');
65
66        let a = parts
67            .next()
68            .and_then(|s| s.trim().parse::<f64>().ok())
69            .ok_or_else(|| format!("{}: could not get a from {}", ERROR, s))?;
70
71        let b = parts
72            .next()
73            .and_then(|s| s.trim().parse::<f64>().ok())
74            .ok_or_else(|| format!("{}: could not get b from {}", ERROR, s))?;
75
76        let c = parts
77            .next()
78            .and_then(|s| s.trim().parse::<f64>().ok())
79            .ok_or_else(|| format!("{}: could not get c from {}", ERROR, s))?;
80
81        if parts.next().is_some() {
82            return Err(format!("{}: too many numbers inputted in {}", ERROR, s).into());
83        }
84
85        Ok(PgLine { a, b, c })
86    }
87}
88
89impl PgLine {
90    fn from_bytes(mut bytes: &[u8]) -> Result<PgLine, BoxDynError> {
91        let a = bytes.get_f64();
92        let b = bytes.get_f64();
93        let c = bytes.get_f64();
94        Ok(PgLine { a, b, c })
95    }
96
97    fn serialize(&self, buff: &mut PgArgumentBuffer) -> Result<(), BoxDynError> {
98        buff.extend_from_slice(&self.a.to_be_bytes());
99        buff.extend_from_slice(&self.b.to_be_bytes());
100        buff.extend_from_slice(&self.c.to_be_bytes());
101        Ok(())
102    }
103
104    #[cfg(test)]
105    fn serialize_to_vec(&self) -> Vec<u8> {
106        let mut buff = PgArgumentBuffer::default();
107        self.serialize(&mut buff).unwrap();
108        buff.to_vec()
109    }
110}
111
112#[cfg(test)]
113mod line_tests {
114
115    use std::str::FromStr;
116
117    use super::PgLine;
118
119    const LINE_BYTES: &[u8] = &[
120        63, 241, 153, 153, 153, 153, 153, 154, 64, 1, 153, 153, 153, 153, 153, 154, 64, 10, 102,
121        102, 102, 102, 102, 102,
122    ];
123
124    #[test]
125    fn can_deserialise_line_type_bytes() {
126        let line = PgLine::from_bytes(LINE_BYTES).unwrap();
127        assert_eq!(
128            line,
129            PgLine {
130                a: 1.1,
131                b: 2.2,
132                c: 3.3
133            }
134        )
135    }
136
137    #[test]
138    fn can_deserialise_line_type_str() {
139        let line = PgLine::from_str("{ 1, 2, 3 }").unwrap();
140        assert_eq!(
141            line,
142            PgLine {
143                a: 1.0,
144                b: 2.0,
145                c: 3.0
146            }
147        );
148    }
149
150    #[test]
151    fn cannot_deserialise_line_too_few_numbers() {
152        let input_str = "{ 1, 2 }";
153        let line = PgLine::from_str(input_str);
154        assert!(line.is_err());
155        if let Err(err) = line {
156            assert_eq!(
157                err.to_string(),
158                format!("error decoding LINE: could not get c from {input_str}")
159            )
160        }
161    }
162
163    #[test]
164    fn cannot_deserialise_line_too_many_numbers() {
165        let input_str = "{ 1, 2, 3, 4 }";
166        let line = PgLine::from_str(input_str);
167        assert!(line.is_err());
168        if let Err(err) = line {
169            assert_eq!(
170                err.to_string(),
171                format!("error decoding LINE: too many numbers inputted in {input_str}")
172            )
173        }
174    }
175
176    #[test]
177    fn cannot_deserialise_line_invalid_numbers() {
178        let input_str = "{ 1, 2, three }";
179        let line = PgLine::from_str(input_str);
180        assert!(line.is_err());
181        if let Err(err) = line {
182            assert_eq!(
183                err.to_string(),
184                format!("error decoding LINE: could not get c from {input_str}")
185            )
186        }
187    }
188
189    #[test]
190    fn can_deserialise_line_type_str_float() {
191        let line = PgLine::from_str("{1.1, 2.2, 3.3}").unwrap();
192        assert_eq!(
193            line,
194            PgLine {
195                a: 1.1,
196                b: 2.2,
197                c: 3.3
198            }
199        );
200    }
201
202    #[test]
203    fn can_serialise_line_type() {
204        let line = PgLine {
205            a: 1.1,
206            b: 2.2,
207            c: 3.3,
208        };
209        assert_eq!(line.serialize_to_vec(), LINE_BYTES,)
210    }
211}