@@ -4,11 +4,9 @@ use std::time::Duration;
44
55use bytes:: Bytes ;
66use futures_util:: stream:: BoxStream ;
7- use objectstore_types:: ExpirationPolicy ;
7+ use objectstore_types:: { Compression , ExpirationPolicy , scope } ;
88use url:: Url ;
99
10- pub use objectstore_types:: Compression ;
11-
1210const USER_AGENT : & str = concat ! ( "objectstore-client/" , env!( "CARGO_PKG_VERSION" ) ) ;
1311
1412#[ derive( Debug ) ]
@@ -218,28 +216,14 @@ impl Usecase {
218216#[ derive( Debug ) ]
219217pub ( crate ) struct ScopeInner {
220218 usecase : Usecase ,
221- scope : String ,
219+ scopes : scope :: Scopes ,
222220}
223221
224222impl ScopeInner {
225223 #[ inline]
226224 pub ( crate ) fn usecase ( & self ) -> & Usecase {
227225 & self . usecase
228226 }
229-
230- fn as_path_segment ( & self ) -> & str {
231- if self . scope . is_empty ( ) {
232- "_"
233- } else {
234- & self . scope
235- }
236- }
237- }
238-
239- impl std:: fmt:: Display for ScopeInner {
240- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
241- f. write_str ( & self . scope )
242- }
243227}
244228
245229/// A [`Scope`] is a sequence of key-value pairs that defines a (possibly nested) namespace within a
@@ -257,18 +241,16 @@ impl Scope {
257241 pub fn new ( usecase : Usecase ) -> Self {
258242 Self ( Ok ( ScopeInner {
259243 usecase,
260- scope : String :: new ( ) ,
244+ scopes : scope :: Scopes :: empty ( ) ,
261245 } ) )
262246 }
263247
264248 fn for_organization ( usecase : Usecase , organization : u64 ) -> Self {
265- let scope = format ! ( "org={}" , organization) ;
266- Self ( Ok ( ScopeInner { usecase, scope } ) )
249+ Self :: new ( usecase) . push ( "org" , organization)
267250 }
268251
269252 fn for_project ( usecase : Usecase , organization : u64 , project : u64 ) -> Self {
270- let scope = format ! ( "org={};project={}" , organization, project) ;
271- Self ( Ok ( ScopeInner { usecase, scope } ) )
253+ Self :: for_organization ( usecase, organization) . push ( "project" , project)
272254 }
273255
274256 /// Extends this Scope by creating a new sub-scope nested within it.
@@ -277,62 +259,13 @@ impl Scope {
277259 V : std:: fmt:: Display ,
278260 {
279261 let result = self . 0 . and_then ( |mut inner| {
280- Self :: validate_key ( key) ?;
281-
282- let value = value. to_string ( ) ;
283- Self :: validate_value ( & value) ?;
284-
285- if !inner. scope . is_empty ( ) {
286- inner. scope . push ( ';' ) ;
287- }
288- inner. scope . push_str ( key) ;
289- inner. scope . push ( '=' ) ;
290- inner. scope . push_str ( & value) ;
291-
262+ inner. scopes . push ( key, value) ?;
292263 Ok ( inner)
293264 } ) ;
294265
295266 Self ( result)
296267 }
297268
298- /// Characters allowed in a Scope's key and value.
299- /// These are the URL safe characters, except for `.` which we use as separator between
300- /// key and value of Scope components.
301- const ALLOWED_CHARS : & [ u8 ] =
302- b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-()$!+'" ;
303-
304- /// Validates that a scope key contains only allowed characters and is not empty.
305- fn validate_key ( key : & str ) -> crate :: Result < ( ) > {
306- if key. is_empty ( ) {
307- return Err ( crate :: Error :: InvalidScope {
308- message : "Scope key cannot be empty" . to_string ( ) ,
309- } ) ;
310- }
311- if key. bytes ( ) . all ( |b| Self :: ALLOWED_CHARS . contains ( & b) ) {
312- Ok ( ( ) )
313- } else {
314- Err ( crate :: Error :: InvalidScope {
315- message : format ! ( "Invalid scope key '{key}'." ) ,
316- } )
317- }
318- }
319-
320- /// Validates that a scope value contains only allowed characters and is not empty.
321- fn validate_value ( value : & str ) -> crate :: Result < ( ) > {
322- if value. is_empty ( ) {
323- return Err ( crate :: Error :: InvalidScope {
324- message : "Scope value cannot be empty" . to_string ( ) ,
325- } ) ;
326- }
327- if value. bytes ( ) . all ( |b| Self :: ALLOWED_CHARS . contains ( & b) ) {
328- Ok ( ( ) )
329- } else {
330- Err ( crate :: Error :: InvalidScope {
331- message : format ! ( "Invalid scope value '{value}'." ) ,
332- } )
333- }
334- }
335-
336269 /// Creates a session for this scope using the given client.
337270 ///
338271 /// # Errors
@@ -366,9 +299,10 @@ pub(crate) struct ClientInner {
366299/// .timeout(Duration::from_secs(1))
367300/// .propagate_traces(true)
368301/// .build()?;
369- /// let usecase = Usecase::new("my_app");
370302///
371- /// let session = client.session(usecase.for_project(12345, 1337))?;
303+ /// let session = Usecase::new("my_app")
304+ /// .for_project(12345, 1337)
305+ /// .session(&client)?;
372306///
373307/// let response = session.put("hello world").send().await?;
374308///
@@ -439,7 +373,7 @@ impl Session {
439373 . push ( "v1" )
440374 . push ( "objects" )
441375 . push ( & self . scope . usecase . name )
442- . push ( self . scope . as_path_segment ( ) )
376+ . push ( & self . scope . scopes . as_api_path ( ) . to_string ( ) )
443377 . extend ( object_key. split ( "/" ) ) ;
444378 drop ( segments) ;
445379
0 commit comments