@@ -16,6 +16,7 @@ use std::collections::{HashMap, HashSet};
1616use std:: fmt:: Debug ;
1717use std:: fs:: File ;
1818use std:: io:: { prelude:: * , BufReader } ;
19+ use std:: marker:: PhantomData ;
1920use std:: path:: { Path , PathBuf } ;
2021use std:: str:: FromStr ;
2122use thiserror:: Error ;
@@ -154,13 +155,6 @@ impl Debug for BlobWriter<'_> {
154155 }
155156}
156157
157- /// Create an OCI tar+gzip layer.
158- pub struct GzipLayerWriter < ' a > ( Sha256Writer < GzEncoder < BlobWriter < ' a > > > ) ;
159-
160- #[ cfg( feature = "zstd" ) ]
161- /// Writer for a OCI tar+zstd layer.
162- pub struct ZstdLayerWriter < ' a > ( Sha256Writer < zstd:: Encoder < ' static , BlobWriter < ' a > > > ) ;
163-
164158#[ derive( Debug ) ]
165159/// An opened OCI directory.
166160pub struct OciDir {
@@ -280,17 +274,32 @@ impl OciDir {
280274 BlobWriter :: new ( & self . dir )
281275 }
282276
277+ /// Create a layer writer with a custom encoder and
278+ /// media type
279+ pub fn create_custom_layer < ' a , W : WriteComplete < BlobWriter < ' a > > > (
280+ & ' a self ,
281+ create : impl FnOnce ( BlobWriter < ' a > ) -> std:: io:: Result < W > ,
282+ media_type : MediaType ,
283+ ) -> Result < LayerWriter < ' a , W > > {
284+ let bw = BlobWriter :: new ( & self . dir ) ?;
285+ Ok ( LayerWriter :: new ( create ( bw) ?, media_type) )
286+ }
287+
283288 /// Create a writer for a new gzip+tar blob; the contents
284289 /// are not parsed, but are expected to be a tarball.
285- pub fn create_gzip_layer ( & self , c : Option < flate2:: Compression > ) -> Result < GzipLayerWriter > {
286- GzipLayerWriter :: new ( & self . dir , c)
290+ pub fn create_gzip_layer < ' a > (
291+ & ' a self ,
292+ c : Option < flate2:: Compression > ,
293+ ) -> Result < LayerWriter < ' a , GzEncoder < BlobWriter < ' a > > > > {
294+ let creator = |bw : BlobWriter < ' a > | Ok ( GzEncoder :: new ( bw, c. unwrap_or_default ( ) ) ) ;
295+ self . create_custom_layer ( creator, MediaType :: ImageLayerGzip )
287296 }
288297
289298 /// Create a tar output stream, backed by a blob
290299 pub fn create_layer (
291300 & self ,
292301 c : Option < flate2:: Compression > ,
293- ) -> Result < tar:: Builder < GzipLayerWriter > > {
302+ ) -> Result < tar:: Builder < LayerWriter < GzEncoder < BlobWriter > > > > {
294303 Ok ( tar:: Builder :: new ( self . create_gzip_layer ( c) ?) )
295304 }
296305
@@ -299,8 +308,12 @@ impl OciDir {
299308 /// are not parsed, but are expected to be a tarball.
300309 ///
301310 /// This method is only available when the `zstd` feature is enabled.
302- pub fn create_layer_zstd ( & self , compression_level : Option < i32 > ) -> Result < ZstdLayerWriter > {
303- ZstdLayerWriter :: new ( & self . dir , compression_level)
311+ pub fn create_layer_zstd < ' a > (
312+ & ' a self ,
313+ compression_level : Option < i32 > ,
314+ ) -> Result < LayerWriter < ' a , zstd:: Encoder < ' static , BlobWriter < ' a > > > > {
315+ let creator = |bw : BlobWriter < ' a > | zstd:: Encoder :: new ( bw, compression_level. unwrap_or ( 0 ) ) ;
316+ self . create_custom_layer ( creator, MediaType :: ImageLayerZstd )
304317 }
305318
306319 #[ cfg( feature = "zstdmt" ) ]
@@ -312,12 +325,17 @@ impl OciDir {
312325 /// [zstd::Encoder::multithread]]
313326 ///
314327 /// This method is only available when the `zstdmt` feature is enabled.
315- pub fn create_layer_zstd_multithread (
316- & self ,
328+ pub fn create_layer_zstd_multithread < ' a > (
329+ & ' a self ,
317330 compression_level : Option < i32 > ,
318331 n_workers : u32 ,
319- ) -> Result < ZstdLayerWriter > {
320- ZstdLayerWriter :: multithread ( & self . dir , compression_level, n_workers)
332+ ) -> Result < LayerWriter < ' a , zstd:: Encoder < ' static , BlobWriter < ' a > > > > {
333+ let creator = |bw : BlobWriter < ' a > | {
334+ let mut encoder = zstd:: Encoder :: new ( bw, compression_level. unwrap_or ( 0 ) ) ?;
335+ encoder. multithread ( n_workers) ?;
336+ Ok ( encoder)
337+ } ;
338+ self . create_custom_layer ( creator, MediaType :: ImageLayerZstd )
321339 }
322340
323341 /// Add a layer to the top of the image stack. The firsh pushed layer becomes the root.
@@ -715,79 +733,72 @@ impl std::io::Write for BlobWriter<'_> {
715733 }
716734}
717735
718- impl < ' a > GzipLayerWriter < ' a > {
719- /// Create a writer for a gzip compressed layer blob.
720- fn new ( ocidir : & ' a Dir , c : Option < flate2:: Compression > ) -> Result < Self > {
721- let bw = BlobWriter :: new ( ocidir) ?;
722- let enc = flate2:: write:: GzEncoder :: new ( bw, c. unwrap_or_default ( ) ) ;
723- Ok ( Self ( Sha256Writer :: new ( enc) ) )
724- }
736+ /// A writer that can be finalized to return an inner writer.
737+ pub trait WriteComplete < W > : Write {
738+ fn complete ( self ) -> std:: io:: Result < W > ;
739+ }
725740
726- /// Consume this writer, flushing buffered data and put the blob in place.
727- pub fn complete ( self ) -> Result < Layer > {
728- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
729- let blob = enc. finish ( ) ?. complete ( ) ?;
730- Ok ( Layer {
731- blob,
732- uncompressed_sha256,
733- media_type : MediaType :: ImageLayerGzip ,
734- } )
741+ impl < W > WriteComplete < W > for GzEncoder < W >
742+ where
743+ W : Write ,
744+ {
745+ fn complete ( self ) -> std:: io:: Result < W > {
746+ self . finish ( )
735747 }
736748}
737749
738- impl std:: io:: Write for GzipLayerWriter < ' _ > {
739- fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
740- self . 0 . write ( data)
750+ #[ cfg( feature = "zstd" ) ]
751+ impl < W > WriteComplete < W > for zstd:: Encoder < ' _ , W >
752+ where
753+ W : Write ,
754+ {
755+ fn complete ( self ) -> std:: io:: Result < W > {
756+ self . finish ( )
741757 }
758+ }
742759
743- fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
744- self . 0 . flush ( )
745- }
760+ pub struct LayerWriter < ' a , W >
761+ where
762+ W : WriteComplete < BlobWriter < ' a > > ,
763+ {
764+ inner : Sha256Writer < W > ,
765+ media_type : MediaType ,
766+ marker : PhantomData < & ' a ( ) > ,
746767}
747768
748- #[ cfg( feature = "zstd" ) ]
749- impl < ' a > ZstdLayerWriter < ' a > {
750- /// Create a writer for a gzip compressed layer blob.
751- fn new ( ocidir : & ' a Dir , c : Option < i32 > ) -> Result < Self > {
752- let bw = BlobWriter :: new ( ocidir) ?;
753- let encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
754- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
769+ impl < ' a , W > LayerWriter < ' a , W >
770+ where
771+ W : WriteComplete < BlobWriter < ' a > > ,
772+ {
773+ pub fn new ( inner : W , media_type : oci_image:: MediaType ) -> Self {
774+ Self {
775+ inner : Sha256Writer :: new ( inner) ,
776+ media_type,
777+ marker : PhantomData ,
778+ }
755779 }
756780
757- /// Consume this writer, flushing buffered data and put the blob in place.
758781 pub fn complete ( self ) -> Result < Layer > {
759- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
760- let blob = enc. finish ( ) ?. complete ( ) ?;
782+ let ( uncompressed_sha256, enc) = self . inner . finish ( ) ;
783+ let blob = enc. complete ( ) ?. complete ( ) ?;
761784 Ok ( Layer {
762785 blob,
763786 uncompressed_sha256,
764- media_type : MediaType :: ImageLayerZstd ,
787+ media_type : self . media_type ,
765788 } )
766789 }
767790}
768791
769- #[ cfg( feature = "zstdmt" ) ]
770- impl < ' a > ZstdLayerWriter < ' a > {
771- /// Create a writer for a zstd compressed layer blob, with multithreaded compression enabled.
772- ///
773- /// The `n_workers` parameter specifies the number of threads to use for compression, per
774- /// [Encoder::multithread]]
775- fn multithread ( ocidir : & ' a Dir , c : Option < i32 > , n_workers : u32 ) -> Result < Self > {
776- let bw = BlobWriter :: new ( ocidir) ?;
777- let mut encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
778- encoder. multithread ( n_workers) ?;
779- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
780- }
781- }
782-
783- #[ cfg( feature = "zstd" ) ]
784- impl std:: io:: Write for ZstdLayerWriter < ' _ > {
792+ impl < ' a , W > std:: io:: Write for LayerWriter < ' a , W >
793+ where
794+ W : WriteComplete < BlobWriter < ' a > > ,
795+ {
785796 fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
786- self . 0 . write ( data)
797+ self . inner . write ( data)
787798 }
788799
789800 fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
790- self . 0 . flush ( )
801+ self . inner . flush ( )
791802 }
792803}
793804
0 commit comments