18
18
//! Utilities for performing tokio-style buffered IO
19
19
20
20
use crate :: path:: Path ;
21
- use crate :: { ObjectMeta , ObjectStore , PutPayloadMut , WriteMultipart } ;
21
+ use crate :: {
22
+ Attributes , ObjectMeta , ObjectStore , PutMultipartOpts , PutOptions , PutPayloadMut , TagSet ,
23
+ WriteMultipart ,
24
+ } ;
22
25
use bytes:: Bytes ;
23
26
use futures:: future:: { BoxFuture , FutureExt } ;
24
27
use futures:: ready;
@@ -217,6 +220,8 @@ impl AsyncBufRead for BufReader {
217
220
pub struct BufWriter {
218
221
capacity : usize ,
219
222
max_concurrency : usize ,
223
+ attributes : Option < Attributes > ,
224
+ tags : Option < TagSet > ,
220
225
state : BufWriterState ,
221
226
store : Arc < dyn ObjectStore > ,
222
227
}
@@ -252,6 +257,8 @@ impl BufWriter {
252
257
capacity,
253
258
store,
254
259
max_concurrency : 8 ,
260
+ attributes : None ,
261
+ tags : None ,
255
262
state : BufWriterState :: Buffer ( path, PutPayloadMut :: new ( ) ) ,
256
263
}
257
264
}
@@ -266,6 +273,22 @@ impl BufWriter {
266
273
}
267
274
}
268
275
276
+ /// Set the attributes of the uploaded object
277
+ pub fn with_attributes ( self , attributes : Attributes ) -> Self {
278
+ Self {
279
+ attributes : Some ( attributes) ,
280
+ ..self
281
+ }
282
+ }
283
+
284
+ /// Set the tags of the uploaded object
285
+ pub fn with_tags ( self , tags : TagSet ) -> Self {
286
+ Self {
287
+ tags : Some ( tags) ,
288
+ ..self
289
+ }
290
+ }
291
+
269
292
/// Abort this writer, cleaning up any partially uploaded state
270
293
///
271
294
/// # Panic
@@ -306,9 +329,13 @@ impl AsyncWrite for BufWriter {
306
329
if b. content_length ( ) . saturating_add ( buf. len ( ) ) >= cap {
307
330
let buffer = std:: mem:: take ( b) ;
308
331
let path = std:: mem:: take ( path) ;
332
+ let opts = PutMultipartOpts {
333
+ attributes : self . attributes . take ( ) . unwrap_or_default ( ) ,
334
+ tags : self . tags . take ( ) . unwrap_or_default ( ) ,
335
+ } ;
309
336
let store = Arc :: clone ( & self . store ) ;
310
337
self . state = BufWriterState :: Prepare ( Box :: pin ( async move {
311
- let upload = store. put_multipart ( & path) . await ?;
338
+ let upload = store. put_multipart_opts ( & path, opts ) . await ?;
312
339
let mut chunked = WriteMultipart :: new_with_chunk_size ( upload, cap) ;
313
340
for chunk in buffer. freeze ( ) {
314
341
chunked. put ( chunk) ;
@@ -346,9 +373,14 @@ impl AsyncWrite for BufWriter {
346
373
BufWriterState :: Buffer ( p, b) => {
347
374
let buf = std:: mem:: take ( b) ;
348
375
let path = std:: mem:: take ( p) ;
376
+ let opts = PutOptions {
377
+ attributes : self . attributes . take ( ) . unwrap_or_default ( ) ,
378
+ tags : self . tags . take ( ) . unwrap_or_default ( ) ,
379
+ ..Default :: default ( )
380
+ } ;
349
381
let store = Arc :: clone ( & self . store ) ;
350
382
self . state = BufWriterState :: Flush ( Box :: pin ( async move {
351
- store. put ( & path, buf. into ( ) ) . await ?;
383
+ store. put_opts ( & path, buf. into ( ) , opts ) . await ?;
352
384
Ok ( ( ) )
353
385
} ) ) ;
354
386
}
@@ -383,6 +415,7 @@ mod tests {
383
415
use super :: * ;
384
416
use crate :: memory:: InMemory ;
385
417
use crate :: path:: Path ;
418
+ use crate :: { Attribute , GetOptions } ;
386
419
use tokio:: io:: { AsyncBufReadExt , AsyncReadExt , AsyncSeekExt , AsyncWriteExt } ;
387
420
388
421
#[ tokio:: test]
@@ -464,26 +497,54 @@ mod tests {
464
497
}
465
498
}
466
499
500
+ // Note: `BufWriter::with_tags` functionality is tested in `crate::tests::tagging`
467
501
#[ tokio:: test]
468
502
async fn test_buf_writer ( ) {
469
503
let store = Arc :: new ( InMemory :: new ( ) ) as Arc < dyn ObjectStore > ;
470
504
let path = Path :: from ( "file.txt" ) ;
505
+ let attributes = Attributes :: from_iter ( [
506
+ ( Attribute :: ContentType , "text/html" ) ,
507
+ ( Attribute :: CacheControl , "max-age=604800" ) ,
508
+ ] ) ;
471
509
472
510
// Test put
473
- let mut writer = BufWriter :: with_capacity ( Arc :: clone ( & store) , path. clone ( ) , 30 ) ;
511
+ let mut writer = BufWriter :: with_capacity ( Arc :: clone ( & store) , path. clone ( ) , 30 )
512
+ . with_attributes ( attributes. clone ( ) ) ;
474
513
writer. write_all ( & [ 0 ; 20 ] ) . await . unwrap ( ) ;
475
514
writer. flush ( ) . await . unwrap ( ) ;
476
515
writer. write_all ( & [ 0 ; 5 ] ) . await . unwrap ( ) ;
477
516
writer. shutdown ( ) . await . unwrap ( ) ;
478
- assert_eq ! ( store. head( & path) . await . unwrap( ) . size, 25 ) ;
517
+ let response = store
518
+ . get_opts (
519
+ & path,
520
+ GetOptions {
521
+ head : true ,
522
+ ..Default :: default ( )
523
+ } ,
524
+ )
525
+ . await
526
+ . unwrap ( ) ;
527
+ assert_eq ! ( response. meta. size, 25 ) ;
528
+ assert_eq ! ( response. attributes, attributes) ;
479
529
480
530
// Test multipart
481
- let mut writer = BufWriter :: with_capacity ( Arc :: clone ( & store) , path. clone ( ) , 30 ) ;
531
+ let mut writer = BufWriter :: with_capacity ( Arc :: clone ( & store) , path. clone ( ) , 30 )
532
+ . with_attributes ( attributes. clone ( ) ) ;
482
533
writer. write_all ( & [ 0 ; 20 ] ) . await . unwrap ( ) ;
483
534
writer. flush ( ) . await . unwrap ( ) ;
484
535
writer. write_all ( & [ 0 ; 20 ] ) . await . unwrap ( ) ;
485
536
writer. shutdown ( ) . await . unwrap ( ) ;
486
-
487
- assert_eq ! ( store. head( & path) . await . unwrap( ) . size, 40 ) ;
537
+ let response = store
538
+ . get_opts (
539
+ & path,
540
+ GetOptions {
541
+ head : true ,
542
+ ..Default :: default ( )
543
+ } ,
544
+ )
545
+ . await
546
+ . unwrap ( ) ;
547
+ assert_eq ! ( response. meta. size, 40 ) ;
548
+ assert_eq ! ( response. attributes, attributes) ;
488
549
}
489
550
}
0 commit comments