@@ -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<'a> Debug for BlobWriter<'a> {
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 > > > {
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+ Ok ( 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+ Ok ( 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.
@@ -682,79 +700,72 @@ impl<'a> std::io::Write for BlobWriter<'a> {
682700 }
683701}
684702
685- impl < ' a > GzipLayerWriter < ' a > {
686- /// Create a writer for a gzip compressed layer blob.
687- fn new ( ocidir : & ' a Dir , c : Option < flate2:: Compression > ) -> Result < Self > {
688- let bw = BlobWriter :: new ( ocidir) ?;
689- let enc = flate2:: write:: GzEncoder :: new ( bw, c. unwrap_or_default ( ) ) ;
690- Ok ( Self ( Sha256Writer :: new ( enc) ) )
691- }
703+ /// A writer that can be finalized to return an inner writer.
704+ pub trait WriteComplete < W > : Write {
705+ fn complete ( self ) -> std:: io:: Result < W > ;
706+ }
692707
693- /// Consume this writer, flushing buffered data and put the blob in place.
694- pub fn complete ( self ) -> Result < Layer > {
695- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
696- let blob = enc. finish ( ) ?. complete ( ) ?;
697- Ok ( Layer {
698- blob,
699- uncompressed_sha256,
700- media_type : MediaType :: ImageLayerGzip ,
701- } )
708+ impl < W > WriteComplete < W > for GzEncoder < W >
709+ where
710+ W : Write ,
711+ {
712+ fn complete ( self ) -> std:: io:: Result < W > {
713+ self . finish ( )
702714 }
703715}
704716
705- impl < ' a > std:: io:: Write for GzipLayerWriter < ' a > {
706- fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
707- self . 0 . write ( data)
717+ #[ cfg( feature = "zstd" ) ]
718+ impl < ' a , W > WriteComplete < W > for zstd:: Encoder < ' a , W >
719+ where
720+ W : Write ,
721+ {
722+ fn complete ( self ) -> std:: io:: Result < W > {
723+ self . finish ( )
708724 }
725+ }
709726
710- fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
711- self . 0 . flush ( )
712- }
727+ pub struct LayerWriter < ' a , W >
728+ where
729+ W : WriteComplete < BlobWriter < ' a > > ,
730+ {
731+ inner : Sha256Writer < W > ,
732+ media_type : MediaType ,
733+ marker : PhantomData < & ' a ( ) > ,
713734}
714735
715- #[ cfg( feature = "zstd" ) ]
716- impl < ' a > ZstdLayerWriter < ' a > {
717- /// Create a writer for a gzip compressed layer blob.
718- fn new ( ocidir : & ' a Dir , c : Option < i32 > ) -> Result < Self > {
719- let bw = BlobWriter :: new ( ocidir) ?;
720- let encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
721- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
736+ impl < ' a , W > LayerWriter < ' a , W >
737+ where
738+ W : WriteComplete < BlobWriter < ' a > > ,
739+ {
740+ pub fn new ( inner : W , media_type : oci_image:: MediaType ) -> Self {
741+ Self {
742+ inner : Sha256Writer :: new ( inner) ,
743+ media_type,
744+ marker : PhantomData ,
745+ }
722746 }
723747
724- /// Consume this writer, flushing buffered data and put the blob in place.
725748 pub fn complete ( self ) -> Result < Layer > {
726- let ( uncompressed_sha256, enc) = self . 0 . finish ( ) ;
727- let blob = enc. finish ( ) ?. complete ( ) ?;
749+ let ( uncompressed_sha256, enc) = self . inner . finish ( ) ;
750+ let blob = enc. complete ( ) ?. complete ( ) ?;
728751 Ok ( Layer {
729752 blob,
730753 uncompressed_sha256,
731- media_type : MediaType :: ImageLayerZstd ,
754+ media_type : self . media_type ,
732755 } )
733756 }
734757}
735758
736- #[ cfg( feature = "zstdmt" ) ]
737- impl < ' a > ZstdLayerWriter < ' a > {
738- /// Create a writer for a zstd compressed layer blob, with multithreaded compression enabled.
739- ///
740- /// The `n_workers` parameter specifies the number of threads to use for compression, per
741- /// [Encoder::multithread]]
742- fn multithread ( ocidir : & ' a Dir , c : Option < i32 > , n_workers : u32 ) -> Result < Self > {
743- let bw = BlobWriter :: new ( ocidir) ?;
744- let mut encoder = zstd:: Encoder :: new ( bw, c. unwrap_or ( 0 ) ) ?;
745- encoder. multithread ( n_workers) ?;
746- Ok ( Self ( Sha256Writer :: new ( encoder) ) )
747- }
748- }
749-
750- #[ cfg( feature = "zstd" ) ]
751- impl < ' a > std:: io:: Write for ZstdLayerWriter < ' a > {
759+ impl < ' a , W > std:: io:: Write for LayerWriter < ' a , W >
760+ where
761+ W : WriteComplete < BlobWriter < ' a > > ,
762+ {
752763 fn write ( & mut self , data : & [ u8 ] ) -> std:: io:: Result < usize > {
753- self . 0 . write ( data)
764+ self . inner . write ( data)
754765 }
755766
756767 fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
757- self . 0 . flush ( )
768+ self . inner . flush ( )
758769 }
759770}
760771
0 commit comments