1#[cfg(feature = "async")]
9mod async_adapters {
10 use crate::Collection;
11 use crate::api::client::{
12 CollectionsClient, ItemsClient, PagedCollectionsClient, StreamCollectionsClient,
13 StreamItemsClient,
14 };
15 use crate::api::{Item, ItemCollection, Search};
16 use async_stream::try_stream;
17 use futures_core::Stream;
18 use std::future::Future;
19
20 #[derive(Debug)]
22 pub struct PagedItemsStream<T> {
23 inner: T,
24 }
25
26 impl<T> PagedItemsStream<T> {
27 pub fn new(inner: T) -> Self {
29 Self { inner }
30 }
31
32 pub fn into_inner(self) -> T {
34 self.inner
35 }
36 }
37
38 impl<T> StreamItemsClient for PagedItemsStream<T>
39 where
40 T: ItemsClient + Clone + Send + Sync,
41 T::Error: Send,
42 {
43 type Error = T::Error;
44
45 fn search_stream(
46 &self,
47 search: Search,
48 ) -> impl Future<
49 Output = Result<impl Stream<Item = Result<Item, T::Error>> + Send, T::Error>,
50 > + Send {
51 let client = self.inner.clone();
52 async move {
53 let page = client.search(search.clone()).await?;
54 Ok(stream_pages(client, search, page))
55 }
56 }
57 }
58
59 pub fn stream_pages<T>(
61 client: T,
62 initial_search: Search,
63 initial_page: ItemCollection,
64 ) -> impl Stream<Item = Result<Item, T::Error>> + Send
65 where
66 T: ItemsClient + Clone + Send + Sync,
67 T::Error: Send,
68 {
69 try_stream! {
70 let mut page = initial_page;
71 let mut current_search = initial_search;
72 loop {
73 if page.items.is_empty() {
74 break;
75 }
76 let next = page.next.clone();
77 for item in page.items {
78 yield item;
79 }
80 match next {
81 Some(next_fields) => {
82 current_search.additional_fields.extend(next_fields);
83 page = client.search(current_search.clone()).await?;
84 }
85 None => break,
86 }
87 }
88 }
89 }
90
91 impl<T> StreamCollectionsClient for T
93 where
94 T: CollectionsClient + Clone + Send + Sync,
95 T::Error: Send,
96 {
97 type Error = T::Error;
98
99 fn collections_stream(
100 &self,
101 ) -> impl Future<
102 Output = Result<impl Stream<Item = Result<Collection, T::Error>> + Send, T::Error>,
103 > + Send {
104 let client = self.clone();
105 async move {
106 let collections = client.collections().await?;
107 Ok(futures::stream::iter(collections.into_iter().map(Ok)))
108 }
109 }
110 }
111
112 pub fn stream_pages_collections<T>(
114 client: T,
115 initial_page: Vec<Collection>,
116 initial_token: Option<String>,
117 ) -> impl Stream<Item = Result<Collection, T::Error>> + Send
118 where
119 T: PagedCollectionsClient + Clone + Send + Sync,
120 T::Error: Send,
121 {
122 try_stream! {
123 let mut page = initial_page;
124 let mut cursor = initial_token;
125 loop {
126 if page.is_empty() {
127 break;
128 }
129 let next_cursor = cursor;
130 for collection in page {
131 yield collection;
132 }
133 match next_cursor {
134 Some(t) => {
135 let (next_page, next_t) = client.collections_page(Some(t)).await?;
136 page = next_page;
137 cursor = next_t;
138 }
139 None => break,
140 }
141 }
142 }
143 }
144}
145
146#[cfg(feature = "async")]
147pub use async_adapters::{PagedItemsStream, stream_pages, stream_pages_collections};
148
149#[cfg(feature = "geoarrow")]
150mod geoarrow_adapters {
151 use super::super::client::{ArrowItemsClient, ItemsClient};
152 use super::super::{Item, ItemCollection, Search};
153 use std::future::Future;
154
155 #[allow(missing_debug_implementations)]
158 pub struct RecordBatchReaderAdapter<I> {
159 inner: I,
160 schema: arrow_schema::SchemaRef,
161 }
162
163 impl<I> RecordBatchReaderAdapter<I> {
164 pub fn new(inner: I, schema: arrow_schema::SchemaRef) -> Self {
166 Self { inner, schema }
167 }
168 }
169
170 impl<I, E> Iterator for RecordBatchReaderAdapter<I>
171 where
172 I: Iterator<Item = Result<arrow_array::RecordBatch, E>>,
173 E: std::error::Error + Send + Sync + 'static,
174 {
175 type Item = Result<arrow_array::RecordBatch, arrow_schema::ArrowError>;
176
177 fn next(&mut self) -> Option<Self::Item> {
178 self.inner
179 .next()
180 .map(|r| r.map_err(|e| arrow_schema::ArrowError::ExternalError(Box::new(e))))
181 }
182 }
183
184 impl<I, E> arrow_array::RecordBatchReader for RecordBatchReaderAdapter<I>
185 where
186 I: Iterator<Item = Result<arrow_array::RecordBatch, E>>,
187 E: std::error::Error + Send + Sync + 'static,
188 {
189 fn schema(&self) -> arrow_schema::SchemaRef {
190 self.schema.clone()
191 }
192 }
193
194 impl<T> ItemsClient for T
195 where
196 T: ArrowItemsClient + Send + Sync,
197 T::Error: std::error::Error + Send + Sync + 'static,
198 {
199 type Error = crate::Error;
200
201 fn search(
202 &self,
203 search: Search,
204 ) -> impl Future<Output = Result<ItemCollection, Self::Error>> + Send {
205 let result: Result<Vec<Item>, crate::Error> = self
206 .search_to_arrow(search)
207 .map_err(|err| crate::Error::ArrowAdapterClient(Box::new(err)))
208 .and_then(crate::geoarrow::json::from_record_batch_reader);
209 async move { Ok(ItemCollection::from(result?)) }
210 }
211 }
212
213 #[cfg(feature = "async")]
214 mod async_geoarrow_adapters {
215 use super::ArrowItemsClient;
216 use crate::api::client::StreamItemsClient;
217 use crate::api::{Item, Search};
218 use futures_core::Stream;
219 use std::future::Future;
220 use std::pin::Pin;
221 use std::task::{Context, Poll};
222
223 struct ArrowItemStream<R> {
224 reader: R,
225 current_items: std::vec::IntoIter<Item>,
226 }
227
228 impl<R> ArrowItemStream<R> {
229 fn new(reader: R) -> Self {
230 Self {
231 reader,
232 current_items: Vec::new().into_iter(),
233 }
234 }
235 }
236
237 impl<R> Unpin for ArrowItemStream<R> {}
238
239 impl<R> Stream for ArrowItemStream<R>
240 where
241 R: arrow_array::RecordBatchReader,
242 {
243 type Item = Result<Item, crate::Error>;
244
245 fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
246 let this = self.get_mut();
247 loop {
248 if let Some(item) = this.current_items.next() {
249 return Poll::Ready(Some(Ok(item)));
250 }
251
252 match this.reader.next() {
253 Some(Ok(batch)) => {
254 match crate::geoarrow::json::record_batch_to_json_rows(batch) {
255 Ok(items) => {
256 this.current_items = items.into_iter();
257 }
258 Err(err) => return Poll::Ready(Some(Err(err))),
259 }
260 }
261 Some(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
262 None => return Poll::Ready(None),
263 }
264 }
265 }
266 }
267
268 impl<T> StreamItemsClient for T
269 where
270 T: ArrowItemsClient + Send + Sync,
271 T::Error: std::error::Error + Send + Sync + 'static,
272 for<'a> T::RecordBatchStream<'a>: Send,
273 {
274 type Error = crate::Error;
275
276 fn search_stream(
277 &self,
278 search: Search,
279 ) -> impl Future<
280 Output = Result<impl Stream<Item = Result<Item, Self::Error>> + Send, Self::Error>,
281 > + Send {
282 let reader = self
283 .search_to_arrow(search)
284 .map_err(|err| crate::Error::ArrowAdapterClient(Box::new(err)));
285 async move { Ok(ArrowItemStream::new(reader?)) }
286 }
287 }
288 }
289}
290
291#[cfg(feature = "geoarrow")]
292pub use geoarrow_adapters::RecordBatchReaderAdapter;