1use crate::{Error, Fields, Filter, Items, Search, Sortby};
4use geojson::Geometry;
5use pyo3::{
6 exceptions::{PyException, PyValueError},
7 types::PyDict,
8 Bound, FromPyObject, PyErr, PyResult,
9};
10use stac::Bbox;
11
12#[allow(clippy::too_many_arguments)]
14pub fn search<'py>(
15 intersects: Option<StringOrDict<'py>>,
16 ids: Option<StringOrList>,
17 collections: Option<StringOrList>,
18 limit: Option<u64>,
19 bbox: Option<Vec<f64>>,
20 datetime: Option<String>,
21 include: Option<StringOrList>,
22 exclude: Option<StringOrList>,
23 sortby: Option<StringOrList>,
24 filter: Option<StringOrDict<'py>>,
25 query: Option<Bound<'py, PyDict>>,
26 kwargs: Option<Bound<'py, PyDict>>,
27) -> PyResult<Search> {
28 let mut fields = Fields::default();
29 if let Some(include) = include {
30 fields.include = include.into();
31 }
32 if let Some(exclude) = exclude {
33 fields.exclude = exclude.into();
34 }
35 let fields = if fields.include.is_empty() && fields.exclude.is_empty() {
36 None
37 } else {
38 Some(fields)
39 };
40 let query = query
41 .map(|query| pythonize::depythonize(&query))
42 .transpose()?;
43 let bbox = bbox.map(Bbox::try_from).transpose().map_err(Error::from)?;
44 let sortby = sortby
45 .map(|sortby| {
46 Vec::<String>::from(sortby)
47 .into_iter()
48 .map(|s| s.parse::<Sortby>().unwrap()) .collect::<Vec<_>>()
50 })
51 .unwrap_or_default();
52 let filter = filter
53 .map(|filter| match filter {
54 StringOrDict::Dict(cql_json) => pythonize::depythonize(&cql_json).map(Filter::Cql2Json),
55 StringOrDict::String(cql2_text) => Ok(Filter::Cql2Text(cql2_text)),
56 })
57 .transpose()?;
58 let filter = filter
59 .map(|filter| filter.into_cql2_json())
60 .transpose()
61 .map_err(Error::from)?;
62 let mut items = Items {
63 limit,
64 bbox,
65 datetime,
66 query,
67 fields,
68 sortby,
69 filter,
70 ..Default::default()
71 };
72 if let Some(kwargs) = kwargs {
73 items.additional_fields = pythonize::depythonize(&kwargs)?;
74 }
75
76 let intersects = intersects
77 .map(|intersects| match intersects {
78 StringOrDict::Dict(json) => pythonize::depythonize(&json)
79 .map_err(PyErr::from)
80 .and_then(|json| {
81 Geometry::from_json_object(json)
82 .map_err(|err| PyValueError::new_err(err.to_string()))
83 }),
84 StringOrDict::String(s) => s
85 .parse::<Geometry>()
86 .map_err(|err| PyValueError::new_err(err.to_string())),
87 })
88 .transpose()?;
89 let ids = ids.map(|ids| ids.into()).unwrap_or_default();
90 let collections = collections.map(|ids| ids.into()).unwrap_or_default();
91 Ok(Search {
92 items,
93 intersects,
94 ids,
95 collections,
96 })
97}
98
99#[derive(Debug, FromPyObject)]
103pub enum StringOrDict<'py> {
104 String(String),
106
107 Dict(Bound<'py, PyDict>),
109}
110
111#[derive(Debug, FromPyObject)]
115pub enum StringOrList {
116 String(String),
118
119 List(Vec<String>),
121}
122
123impl From<StringOrList> for Vec<String> {
124 fn from(value: StringOrList) -> Vec<String> {
125 match value {
126 StringOrList::List(list) => list,
127 StringOrList::String(s) => vec![s],
128 }
129 }
130}
131
132impl From<Error> for PyErr {
133 fn from(value: Error) -> Self {
134 PyException::new_err(value.to_string())
135 }
136}