1+ //! A STAC API client.
2+
13use crate :: { Error , GetItems , Item , ItemCollection , Items , Result , Search , UrlBuilder } ;
24use async_stream:: try_stream;
35use futures:: { pin_mut, Stream , StreamExt } ;
@@ -6,7 +8,9 @@ use reqwest::{header::HeaderMap, IntoUrl, Method, StatusCode};
68use serde:: { de:: DeserializeOwned , Serialize } ;
79use serde_json:: { Map , Value } ;
810use stac:: { Collection , Href , Link , Links } ;
11+ use std:: pin:: Pin ;
912use tokio:: {
13+ runtime:: { Builder , Runtime } ,
1014 sync:: mpsc:: { self , error:: SendError } ,
1115 task:: JoinHandle ,
1216} ;
@@ -21,6 +25,17 @@ pub struct Client {
2125 url_builder : UrlBuilder ,
2226}
2327
28+ /// A client for interacting with STAC APIs without async.
29+ #[ derive( Debug ) ]
30+ pub struct BlockingClient ( Client ) ;
31+
32+ /// A blocking iterator over items.
33+ #[ allow( missing_debug_implementations) ]
34+ pub struct BlockingIterator {
35+ runtime : Runtime ,
36+ stream : Pin < Box < dyn Stream < Item = Result < Item > > > > ,
37+ }
38+
2439impl Client {
2540 /// Creates a new API client.
2641 ///
@@ -127,12 +142,12 @@ impl Client {
127142 ///
128143 /// let client = Client::new("https://planetarycomputer.microsoft.com/api/stac/v1").unwrap();
129144 /// let mut search = Search { collections: Some(vec!["sentinel-2-l2a".to_string()]), ..Default::default() };
130- /// search.items.limit = Some(1);
131145 /// # tokio_test::block_on(async {
132146 /// let items: Vec<_> = client
133147 /// .search(search)
134148 /// .await
135149 /// .unwrap()
150+ /// .take(1)
136151 /// .map(|result| result.unwrap())
137152 /// .collect()
138153 /// .await;
@@ -226,6 +241,57 @@ impl Client {
226241 }
227242}
228243
244+ impl BlockingClient {
245+ /// Creates a new blocking client.
246+ ///
247+ /// # Examples
248+ ///
249+ /// ```
250+ /// use stac_api::BlockingClient;
251+ ///
252+ /// let client = BlockingClient::new("https://planetarycomputer.microsoft.com/api/stac/vi").unwrap();
253+ /// ```
254+ pub fn new ( url : & str ) -> Result < BlockingClient > {
255+ Client :: new ( url) . map ( Self )
256+ }
257+
258+ /// Searches an API, returning an iterable of items.
259+ ///
260+ /// To prevent fetching _all_ the items (which might be a lot), it is recommended to pass a `max_items`.
261+ ///
262+ /// # Examples
263+ ///
264+ /// ```no_run
265+ /// use stac_api::{Search, BlockingClient};
266+ ///
267+ /// let client = BlockingClient::new("https://planetarycomputer.microsoft.com/api/stac/v1").unwrap();
268+ /// let mut search = Search { collections: Some(vec!["sentinel-2-l2a".to_string()]), ..Default::default() };
269+ /// let items: Vec<_> = client
270+ /// .search(search)
271+ /// .unwrap()
272+ /// .map(|result| result.unwrap())
273+ /// .take(1)
274+ /// .collect();
275+ /// assert_eq!(items.len(), 1);
276+ /// ```
277+ pub fn search ( & self , search : Search ) -> Result < BlockingIterator > {
278+ let runtime = Builder :: new_current_thread ( ) . enable_all ( ) . build ( ) ?;
279+ let stream = runtime. block_on ( async move { self . 0 . search ( search) . await } ) ?;
280+ Ok ( BlockingIterator {
281+ runtime,
282+ stream : Box :: pin ( stream) ,
283+ } )
284+ }
285+ }
286+
287+ impl Iterator for BlockingIterator {
288+ type Item = Result < Item > ;
289+
290+ fn next ( & mut self ) -> Option < Self :: Item > {
291+ self . runtime . block_on ( self . stream . next ( ) )
292+ }
293+ }
294+
229295fn stream_items (
230296 client : Client ,
231297 page : ItemCollection ,
0 commit comments