Skip to content

Commit e5146e1

Browse files
author
AGAEV Denis E
committed
Added build_point realization
1 parent 4c71e7b commit e5146e1

File tree

3 files changed

+110
-61
lines changed

3 files changed

+110
-61
lines changed

python/psqlpy/_internal/extra_types.pyi

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Sequence, Union
1+
import typing
22

33
from typing_extensions import Self
44

@@ -73,9 +73,9 @@ class PyJSONB:
7373

7474
def __init__(
7575
self: Self,
76-
value: Union[
77-
dict[str, Any],
78-
list[dict[str, Any]],
76+
value: typing.Union[
77+
dict[str, typing.Any],
78+
list[dict[str, typing.Any]],
7979
],
8080
) -> None:
8181
"""Create new instance of PyJSON.B.
@@ -91,9 +91,9 @@ class PyJSON:
9191

9292
def __init__(
9393
self: Self,
94-
value: Union[
95-
dict[str, Any],
96-
list[dict[str, Any]],
94+
value: typing.Union[
95+
dict[str, typing.Any],
96+
list[dict[str, typing.Any]],
9797
],
9898
) -> None:
9999
"""Create new instance of PyJSON.
@@ -124,77 +124,87 @@ class PyMacAddr8:
124124
- `value`: value for MACADDR8 field.
125125
"""
126126

127+
Coordinates: typing.TypeAlias = typing.Union[
128+
list[int | float],
129+
set[int | float],
130+
tuple[int | float],
131+
]
132+
127133
class PyPoint:
128134
"""Represent point field in PostgreSQL and Point in Rust."""
129135

130136
def __init__(
131137
self: Self,
132-
value: Sequence[float],
138+
value: Coordinates,
133139
) -> None:
134140
"""Create new instance of PyPoint.
135141
136-
It accepts any sequence of two float numbers.
142+
It accepts any pair(List, Tuple or Set)
143+
of int/float numbers in every combination.
137144
138145
### Parameters:
139-
- `value`: sequence of two float numbers.
146+
- `value`: pair of int/float numbers in every combination.
140147
"""
141148

142149
class PyBox:
143150
"""Represent box field in PostgreSQL and Rect in Rust."""
144151

145152
def __init__(
146153
self: Self,
147-
value: Union[
148-
Sequence[Sequence[float]],
149-
Sequence[float],
154+
value: typing.Union[
155+
typing.Union[list[Coordinates], set[Coordinates], tuple[Coordinates]],
156+
Coordinates,
150157
],
151158
) -> None:
152159
"""Create new instance of PyBox.
153160
154161
You need to pass any of this structures:
155-
- sequence of two sequences, each with pair of float numbers
156-
- sequence of two pairs of float
162+
- sequence of two sequences,
163+
each with pair of int/float numbers in every combination
164+
- sequence of two pairs of int/float in every combination
157165
158166
### Parameters:
159-
- `value`: any valid sequence with two pairs of float numbers.
167+
- `value`: any valid sequence with two pairs
168+
of int/float numbers in every combination.
160169
"""
161170

162171
class PyPath:
163172
"""Represent path field in PostgreSQL and LineString in Rust."""
164173

165174
def __init__(
166175
self: Self,
167-
value: Union[
168-
Sequence[Sequence[float]],
169-
Sequence[float],
176+
value: typing.Union[
177+
typing.Union[list[Coordinates], set[Coordinates], tuple[Coordinates]],
178+
Coordinates,
170179
],
171180
) -> None:
172181
"""Create new instance of PyPath.
173182
174183
You need to pass any of this structures:
175-
- sequence of sequences, each with pair of float numbers
176-
- sequence with pairs of float numbers
184+
- sequence of sequences, each with pair of int/float numbers in every combination
185+
- sequence with pairs of int/float numbers in every combination
177186
178187
### Parameters:
179-
- `value`: any valid structure with float numbers.
188+
- `value`: any valid structure with int/float numbers in every combination.
180189
"""
181190

182191
class PyLine:
183192
"""Represent line field in PostgreSQL and Line in Rust."""
184193

185194
def __init__(
186195
self: Self,
187-
value: Union[
188-
Sequence[Sequence[float]],
189-
Sequence[float],
196+
value: typing.Union[
197+
typing.Union[list[Coordinates], set[Coordinates], tuple[Coordinates]],
198+
Coordinates,
190199
],
191200
) -> None:
192201
"""Create new instance of PyLine.
193202
194203
You need to pass any of this structures:
195-
- sequence of three float numbers
196-
- sequence of two sequences, each with pair of float numbers
197-
- sequence with two pairs of float numbers
204+
- sequence of three int/float numbers
205+
- sequence of two sequences,
206+
each with pair of int/float numbers in every combination
207+
- sequence with two pairs of int/float numbers in every combination
198208
199209
### Parameters:
200210
- `value`: any valid structure with float numbers.
@@ -205,37 +215,38 @@ class PyLineSegment:
205215

206216
def __init__(
207217
self: Self,
208-
value: Union[
209-
Sequence[Sequence[float]],
210-
Sequence[float],
218+
value: typing.Union[
219+
typing.Union[list[Coordinates], set[Coordinates], tuple[Coordinates]],
220+
Coordinates,
211221
],
212222
) -> None:
213223
"""Create new instance of PyLineSegment.
214224
215225
You need to pass any of this structures:
216-
- sequence of two sequences, each with pair of float numbers
217-
- sequence with two pairs of float numbers
226+
- sequence of two sequences,
227+
each with pair of int/float numbers in every combination
228+
- sequence with two pairs of int/float numbers in every combination
218229
219230
### Parameters:
220-
- `value`: any valid structure with float numbers.
231+
- `value`: any valid structure with int/float numbers in every combination.
221232
"""
222233

223234
class PyPolygon:
224235
"""Represent polygon field in PostgreSQL and Polygon in Rust."""
225236

226237
def __init__(
227238
self: Self,
228-
value: Union[
229-
Sequence[Sequence[float]],
230-
Sequence[float],
239+
value: typing.Union[
240+
typing.Union[list[Coordinates], set[Coordinates], tuple[Coordinates]],
241+
Coordinates,
231242
],
232243
) -> None:
233244
"""Create new instance of PyPolygon.
234245
235246
You need to pass any of this structures:
236-
- sequence of sequences, each with pair of float numbers
237-
- sequence with pairs of float numbers
247+
- sequence of sequences, each with pair of int/float numbers in every combination
248+
- sequence with pairs of int/float numbers in every combination
238249
239250
### Parameters:
240-
- `value`: any valid structure with float numbers.
251+
- `value`: any valid structure with int/float numbers in every combination.
241252
"""

src/additional_types.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use byteorder::{BigEndian, ReadBytesExt};
2+
use geo_types::{coord, Coord, CoordFloat, CoordNum, Line, LineString, Polygon};
13
use itertools::Itertools;
24
use macaddr::{MacAddr6, MacAddr8};
3-
use geo_types::{coord, Coord, CoordFloat, CoordNum, Line, LineString, Polygon};
4-
use byteorder::{ReadBytesExt, BigEndian};
55
use tokio_postgres::types::{FromSql, Type};
66

77
macro_rules! build_additional_rust_type {
@@ -73,7 +73,7 @@ impl<'a> FromSql<'a> for RustLine {
7373
raw: &'a [u8],
7474
) -> Result<RustLine, Box<dyn std::error::Error + Sync + Send>> {
7575
if raw.len() == 4 {
76-
let mut vec_raw= vec![];
76+
let mut vec_raw = vec![];
7777
vec_raw.extend_from_slice(raw);
7878
let mut buf = vec_raw.as_slice();
7979

@@ -108,14 +108,14 @@ impl<'a> FromSql<'a> for RustPolygon {
108108

109109
let mut vec_raw_coord = vec![];
110110
buf.read_f64_into::<BigEndian>(&mut vec_raw_coord);
111-
111+
112112
let mut vec_coord = vec![];
113113
for (x1, y1) in vec_raw_coord.into_iter().tuples() {
114114
vec_coord.push(coord!(x: x1, y: y1));
115115
}
116116

117117
let polygon_exterior = LineString::new(vec_coord);
118-
let new_polygon = Polygon::new(polygon_exterior, vec![]);
118+
let new_polygon = Polygon::new(polygon_exterior, vec![]);
119119
return Ok(RustPolygon::new(new_polygon));
120120
}
121121
Err("Cannot convert PostgreSQL POLYGON into rust Polygon".into())
@@ -126,7 +126,6 @@ impl<'a> FromSql<'a> for RustPolygon {
126126
}
127127
}
128128

129-
130129
// add macro for creating circles
131130

132131
#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
@@ -138,7 +137,10 @@ pub struct Circle<T: CoordNum = f64> {
138137

139138
impl<T: CoordNum> Circle<T> {
140139
pub fn new(x: T, y: T, r: T) -> Self {
141-
Self {center: coord!(x: x, y: y), radius: r}
140+
Self {
141+
center: coord!(x: x, y: y),
142+
radius: r,
143+
}
142144
}
143145

144146
pub fn center(self) -> Coord<T> {
@@ -186,7 +188,10 @@ impl<T: CoordFloat> Circle<T> {
186188

187189
impl<T: CoordNum> Default for Circle<T> {
188190
fn default() -> Self {
189-
Self {center: coord! {x: T::zero(), y: T::zero()}, radius: T::zero()}
191+
Self {
192+
center: coord! {x: T::zero(), y: T::zero()},
193+
radius: T::zero(),
194+
}
190195
}
191196
}
192197

@@ -213,4 +218,4 @@ impl<'a> FromSql<'a> for Circle {
213218
fn accepts(_ty: &Type) -> bool {
214219
true
215220
}
216-
}
221+
}

src/value_converter.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use chrono::{self, DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
2-
use geo_types::Point;
2+
use geo_types::{point, Point};
33
use macaddr::{MacAddr6, MacAddr8};
44
use postgres_types::{Field, FromSql, Kind};
55
use serde_json::{json, Map, Value};
@@ -10,8 +10,8 @@ use bytes::{BufMut, BytesMut};
1010
use postgres_protocol::types;
1111
use pyo3::{
1212
types::{
13-
PyAnyMethods, PyBool, PyBytes, PyDate, PyDateTime, PyDict, PyDictMethods,
14-
PyFloat, PyInt, PyList, PyListMethods, PySet, PyString, PyTime, PyTuple
13+
PyAnyMethods, PyBool, PyBytes, PyDate, PyDateTime, PyDict, PyDictMethods, PyFloat, PyInt,
14+
PyList, PyListMethods, PySet, PyString, PyTime, PyTuple,
1515
},
1616
Bound, Py, PyAny, Python, ToPyObject,
1717
};
@@ -24,8 +24,8 @@ use crate::{
2424
additional_types::{RustMacAddr6, RustMacAddr8},
2525
exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult},
2626
extra_types::{
27-
BigInt, Integer, PyJSON, PyJSONB, PyMacAddr6, PyMacAddr8, PyText, PyUUID,
28-
PyVarChar, SmallInt,
27+
BigInt, Integer, PyJSON, PyJSONB, PyMacAddr6, PyMacAddr8, PyText, PyUUID, PyVarChar,
28+
SmallInt,
2929
},
3030
};
3131

@@ -851,7 +851,7 @@ pub fn build_python_from_serde_value(
851851
}
852852
}
853853

854-
/// Convert python List or Tuple into geo `Point`.
854+
/// Convert python List, Tuple or Set into geo `Point`.
855855
///
856856
/// # Errors
857857
/// May return error if cannot convert Python type into Rust one.
@@ -863,25 +863,58 @@ pub fn build_point(value: Py<PyAny>) -> RustPSQLDriverPyResult<Point> {
863863
| bind_value.is_instance_of::<PyTuple>()
864864
| bind_value.is_instance_of::<PySet>()
865865
{
866-
let mut result_vec = vec![];
866+
let mut result_vec: Vec<f64> = vec![];
867867
let params = bind_value.extract::<Vec<Py<PyAny>>>()?;
868868

869+
if params.len() != 2 {
870+
return Err(RustPSQLDriverError::PyToRustValueConversionError(
871+
"PyPoint supports only pair of int/float combination.".to_string(),
872+
));
873+
}
874+
869875
for inner in params {
870876
let inner_bind = inner.bind(gil);
877+
871878
if inner_bind.is_instance_of::<PyFloat>() | inner_bind.is_instance_of::<PyInt>() {
872879
let python_dto = py_to_rust(inner_bind)?;
873-
let mut bu = BytesMut::with_capacity(64);
874-
python_dto.to_sql(postgres_types::, &mut bu)
880+
match python_dto {
881+
PythonDTO::PyIntI16(pyint) => result_vec.push(pyint.try_into().unwrap()),
882+
PythonDTO::PyIntI32(pyint) => result_vec.push(pyint.try_into().unwrap()),
883+
PythonDTO::PyIntI64(_) => {
884+
return Err(RustPSQLDriverError::PyToRustValueConversionError(
885+
"Not implemented this type yet".into(),
886+
))
887+
}
888+
PythonDTO::PyIntU32(pyint) => result_vec.push(pyint.try_into().unwrap()),
889+
PythonDTO::PyIntU64(_) => {
890+
return Err(RustPSQLDriverError::PyToRustValueConversionError(
891+
"Not implemented this type yet".into(),
892+
))
893+
}
894+
PythonDTO::PyFloat32(pyfloat) => {
895+
result_vec.push(pyfloat.try_into().unwrap())
896+
}
897+
PythonDTO::PyFloat64(pyfloat) => result_vec.push(pyfloat),
898+
_ => {
899+
return Err(RustPSQLDriverError::PyToRustValueConversionError(
900+
"Incorrect types of point coordinates. It must be int or float"
901+
.into(),
902+
))
903+
}
904+
};
875905
} else {
876906
return Err(RustPSQLDriverError::PyToRustValueConversionError(
877-
"PyJSON supports only list/tuple/set of two int or two float.".to_string(),
907+
"PyPoint supports only list/tuple/set pair of int/float combination."
908+
.to_string(),
878909
));
879910
}
880911
}
881-
Ok(point!(result_vec))
912+
913+
Ok(point!(x: result_vec[0], y: result_vec[1]))
882914
} else {
883915
return Err(RustPSQLDriverError::PyToRustValueConversionError(
884-
"PyPoint must have two int or float values passed in tuple, list or set.".to_string(),
916+
"PyPoint must have pair int/float values combination passed in tuple, list or set."
917+
.to_string(),
885918
));
886919
}
887920
})

0 commit comments

Comments
 (0)