@@ -2,86 +2,87 @@ use crate::{Format, Readable, Result, Writeable};
22use object_store:: { ObjectStore , ObjectStoreScheme , PutResult , path:: Path } ;
33use stac:: Href ;
44use std:: sync:: Arc ;
5+ use url:: Url ;
56
67/// Parses an href into a [StacStore] and a [Path].
7- pub fn parse_href ( href : impl AsRef < Href > ) -> Result < ( StacStore , Path ) > {
8+ pub fn parse_href ( href : impl Into < Href > ) -> Result < ( StacStore , Path ) > {
89 parse_href_opts ( href, [ ] as [ ( & str , & str ) ; 0 ] )
910}
1011
1112/// Parses an href and options into [StacStore] and a [Path].
1213///
1314/// Relative string hrefs are made absolute `file://` hrefs relative to the current directory.`
14- pub fn parse_href_opts < I , K , V > ( href : impl AsRef < Href > , options : I ) -> Result < ( StacStore , Path ) >
15+ pub fn parse_href_opts < I , K , V > ( href : impl Into < Href > , options : I ) -> Result < ( StacStore , Path ) >
1516where
1617 I : IntoIterator < Item = ( K , V ) > ,
1718 K : AsRef < str > ,
1819 V : Into < String > ,
1920{
21+ let mut url = match href. into ( ) {
22+ Href :: Url ( url) => url,
23+ Href :: String ( s) => {
24+ let s = if s. starts_with ( "/" ) {
25+ format ! ( "file://{s}" )
26+ } else {
27+ let path_buf = std:: fs:: canonicalize ( s) ?;
28+ format ! ( "file://{}" , path_buf. display( ) )
29+ } ;
30+ Url :: parse ( & s) ?
31+ }
32+ } ;
2033 let parse = || -> Result < ( Box < dyn ObjectStore > , Path ) > {
21- match href. as_ref ( ) {
22- Href :: Url ( url) => {
23- tracing:: debug!( "parsing url={url}" ) ;
24- // It's technically inefficient to parse it twice, but we're doing this to
25- // then do IO so who cares.
26- let ( scheme, path) =
27- ObjectStoreScheme :: parse ( url) . map_err ( object_store:: Error :: from) ?;
28-
29- #[ cfg( feature = "store-aws" ) ]
30- if matches ! ( scheme, ObjectStoreScheme :: AmazonS3 ) {
31- let mut builder = object_store:: aws:: AmazonS3Builder :: from_env ( ) ;
32- for ( key, value) in options {
33- builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
34- }
35- return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
36- }
34+ tracing:: debug!( "parsing url={url}" ) ;
35+ // It's technically inefficient to parse it twice, but we're doing this to
36+ // then do IO so who cares.
37+ let ( scheme, path) = ObjectStoreScheme :: parse ( & url) . map_err ( object_store:: Error :: from) ?;
3738
38- #[ cfg( feature = "store-azure" ) ]
39- if matches ! ( scheme, ObjectStoreScheme :: AmazonS3 ) {
40- let mut builder = object_store:: azure:: MicrosoftAzureBuilder :: from_env ( ) ;
41- for ( key, value) in options {
42- builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
43- }
44- return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
45- }
46-
47- #[ cfg( feature = "store-gcp" ) ]
48- if matches ! ( scheme, ObjectStoreScheme :: GoogleCloudStorage ) {
49- let mut builder = object_store:: gcp:: GoogleCloudStorageBuilder :: from_env ( ) ;
50- for ( key, value) in options {
51- builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
52- }
53- return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
54- }
39+ #[ cfg( feature = "store-aws" ) ]
40+ if matches ! ( scheme, ObjectStoreScheme :: AmazonS3 ) {
41+ let mut builder = object_store:: aws:: AmazonS3Builder :: from_env ( ) ;
42+ for ( key, value) in options {
43+ builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
44+ }
45+ return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
46+ }
5547
56- let pair = object_store:: parse_url_opts ( url, options) ?;
57- Ok ( pair)
48+ #[ cfg( feature = "store-azure" ) ]
49+ if matches ! ( scheme, ObjectStoreScheme :: AmazonS3 ) {
50+ let mut builder = object_store:: azure:: MicrosoftAzureBuilder :: from_env ( ) ;
51+ for ( key, value) in options {
52+ builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
5853 }
59- Href :: String ( s) => {
60- if s. starts_with ( "/" ) {
61- let pair =
62- object_store:: parse_url_opts ( & format ! ( "file://{s}" ) . parse ( ) ?, options) ?;
63- Ok ( pair)
64- } else {
65- let s = std:: env:: current_dir ( ) ?. join ( s) ;
66- let pair = object_store:: parse_url_opts (
67- & format ! ( "file://{}" , s. display( ) ) . parse ( ) ?,
68- options,
69- ) ?;
70- Ok ( pair)
71- }
54+ return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
55+ }
56+
57+ #[ cfg( feature = "store-gcp" ) ]
58+ if matches ! ( scheme, ObjectStoreScheme :: GoogleCloudStorage ) {
59+ let mut builder = object_store:: gcp:: GoogleCloudStorageBuilder :: from_env ( ) ;
60+ for ( key, value) in options {
61+ builder = builder. with_config ( key. as_ref ( ) . parse ( ) ?, value) ;
7262 }
63+ return Ok ( ( Box :: new ( builder. with_url ( url. to_string ( ) ) . build ( ) ?) , path) ) ;
7364 }
65+
66+ let pair = object_store:: parse_url_opts ( & url, options) ?;
67+ Ok ( pair)
7468 } ;
7569 let ( store, path) = parse ( ) ?;
76- Ok ( ( store. into ( ) , path) )
70+ url. set_path ( "" ) ;
71+ Ok ( ( StacStore :: new ( Arc :: new ( store) , url) , path) )
7772}
7873
7974/// Reads STAC from an [ObjectStore].
8075#[ derive( Debug ) ]
81- pub struct StacStore ( Arc < dyn ObjectStore > ) ;
76+ pub struct StacStore {
77+ store : Arc < dyn ObjectStore > ,
78+ root : Url ,
79+ }
8280
8381impl StacStore {
84- /// Creates a new [StacStore] from an [ObjectStore].
82+ /// Creates a new [StacStore] from an [ObjectStore] and a root href.
83+ ///
84+ /// The root href is used to set the self href on all read STAC values,
85+ /// since we can't get that from the store.
8586 ///
8687 /// # Examples
8788 ///
@@ -90,10 +91,13 @@ impl StacStore {
9091 /// use stac_io::StacStore;
9192 /// use std::sync::Arc;
9293 ///
93- /// let stac_store = StacStore::new(Arc::new(LocalFileSystem::new()));
94+ /// let stac_store = StacStore::new(Arc::new(LocalFileSystem::new()), "file://".parse().unwrap() );
9495 /// ```
95- pub fn new ( store : Arc < dyn ObjectStore > ) -> StacStore {
96- StacStore ( Arc :: new ( store) )
96+ pub fn new ( store : Arc < dyn ObjectStore > , root : Url ) -> StacStore {
97+ StacStore {
98+ store : Arc :: new ( store) ,
99+ root,
100+ }
97101 }
98102
99103 /// Gets a STAC value from the store.
@@ -127,9 +131,10 @@ impl StacStore {
127131 T : Readable ,
128132 {
129133 let path = path. into ( ) ;
130- let get_result = self . 0 . get ( & path) . await ?;
134+ let get_result = self . store . get ( & path) . await ?;
131135 let bytes = get_result. bytes ( ) . await ?;
132- let value: T = format. from_bytes ( bytes) ?;
136+ let mut value: T = format. from_bytes ( bytes) ?;
137+ value. set_self_href ( self . root . join ( path. as_ref ( ) ) ?) ;
133138 Ok ( value)
134139 }
135140
@@ -155,31 +160,37 @@ impl StacStore {
155160 {
156161 let path = path. into ( ) ;
157162 let bytes = format. into_vec ( value) ?;
158- let put_result = self . 0 . put ( & path, bytes. into ( ) ) . await ?;
163+ let put_result = self . store . put ( & path, bytes. into ( ) ) . await ?;
159164 Ok ( put_result)
160165 }
161166}
162167
163- impl < T > From < T > for StacStore
164- where
165- T : ObjectStore ,
166- {
167- fn from ( value : T ) -> Self {
168- StacStore ( Arc :: new ( value) )
169- }
170- }
171-
172168#[ cfg( test) ]
173169mod tests {
174- use super :: StacStore ;
175- use object_store:: local:: LocalFileSystem ;
176- use stac:: Item ;
170+ use stac:: { Item , SelfHref } ;
177171
178172 #[ tokio:: test]
179173 async fn get_local ( ) {
180- let store = StacStore :: from (
181- LocalFileSystem :: new_with_prefix ( std:: env:: current_dir ( ) . unwrap ( ) ) . unwrap ( ) ,
174+ let ( store, path) = super :: parse_href ( "examples/simple-item.json" ) . unwrap ( ) ;
175+ assert_eq ! (
176+ path,
177+ std:: fs:: canonicalize( "examples/simple-item.json" )
178+ . unwrap( )
179+ . to_string_lossy( )
180+ . into_owned( )
181+ . strip_prefix( "/" )
182+ . unwrap( )
183+ . into( )
182184 ) ;
183- let _: Item = store. get ( "examples/simple-item.json" ) . await . unwrap ( ) ;
185+ let item: Item = store. get ( path) . await . unwrap ( ) ;
186+ assert_eq ! (
187+ item. self_href( ) . unwrap( ) . to_string( ) ,
188+ format!(
189+ "file://{}" ,
190+ std:: fs:: canonicalize( "examples/simple-item.json" )
191+ . unwrap( )
192+ . display( )
193+ )
194+ )
184195 }
185196}
0 commit comments