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