Skip to content

Commit

Permalink
Rework ArchiveAsync interface to make it return any archiving errors
Browse files Browse the repository at this point in the history
After experience with the current ArchiveAsync interface while
creating an rclone backend it became clear that it wasn't quite right.

With the old interface, callers did not know when the File had been
archived and thus did not know when to release resources associated
with that file.

This patch changes the interface so that it returns the error
archiving each File. This means the caller can know when the File has
been archived and when to release resources. It returns the error for
each file archived which is useful too.

Fixes mholt#368
  • Loading branch information
ncw committed Feb 7, 2023
1 parent 70981de commit fedac4a
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 24 deletions.
13 changes: 11 additions & 2 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,25 @@ type Archiver interface {
Archive(ctx context.Context, output io.Writer, files []File) error
}

// ArchiveAsyncJob contains a File to be archived and a channel that
// the result of the archiving should be returned on.
type ArchiveAsyncJob struct {
File File
Result chan<- error
}

// ArchiverAsync is an Archiver that can also create archives
// asynchronously by pumping files into a channel as they are
// discovered.
type ArchiverAsync interface {
Archiver

// Use ArchiveAsync if you can't pre-assemble a list of all
// the files for the archive. Close the files channel after
// the files for the archive. Close the jobs channel after
// all the files have been sent.
ArchiveAsync(ctx context.Context, output io.Writer, files <-chan File) error
//
// This won't return until the channel is closed.
ArchiveAsync(ctx context.Context, output io.Writer, jobs <-chan ArchiveAsyncJob) error
}

// Extractor can extract files from an archive.
Expand Down
19 changes: 7 additions & 12 deletions tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,12 @@ func (t Tar) Archive(ctx context.Context, output io.Writer, files []File) error
return nil
}

func (t Tar) ArchiveAsync(ctx context.Context, output io.Writer, files <-chan File) error {
func (t Tar) ArchiveAsync(ctx context.Context, output io.Writer, jobs <-chan ArchiveAsyncJob) error {
tw := tar.NewWriter(output)
defer tw.Close()

for file := range files {
if err := t.writeFileToArchive(ctx, tw, file); err != nil {
if t.ContinueOnError && ctx.Err() == nil { // context errors should always abort
log.Printf("[ERROR] %v", err)
continue
}
return err
}
for job := range jobs {
job.Result <- t.writeFileToArchive(ctx, tw, job.File)
}

return nil
Expand Down Expand Up @@ -234,7 +228,8 @@ func (t Tar) Extract(ctx context.Context, sourceArchive io.Reader, pathsInArchiv

// Interface guards
var (
_ Archiver = (*Tar)(nil)
_ Extractor = (*Tar)(nil)
_ Inserter = (*Tar)(nil)
_ Archiver = (*Tar)(nil)
_ ArchiverAsync = (*Tar)(nil)
_ Extractor = (*Tar)(nil)
_ Inserter = (*Tar)(nil)
)
21 changes: 11 additions & 10 deletions zip.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,13 @@ func (z Zip) Archive(ctx context.Context, output io.Writer, files []File) error
return nil
}

func (z Zip) ArchiveAsync(ctx context.Context, output io.Writer, files <-chan File) error {
func (z Zip) ArchiveAsync(ctx context.Context, output io.Writer, jobs <-chan ArchiveAsyncJob) error {
zw := zip.NewWriter(output)
defer zw.Close()

var i int
for file := range files {
if err := z.archiveOneFile(ctx, zw, i, file); err != nil {
if z.ContinueOnError && ctx.Err() == nil { // context errors should always abort
log.Printf("[ERROR] %v", err)
continue
}
return err
}
for job := range jobs {
job.Result <- z.archiveOneFile(ctx, zw, i, job.File)
i++
}

Expand Down Expand Up @@ -202,7 +196,7 @@ func (z Zip) Extract(ctx context.Context, sourceArchive io.Reader, pathsInArchiv
skipDirs := skipList{}

for i, f := range zr.File {
f := f // make a copy for the Open closure
f := f // make a copy for the Open closure
if err := ctx.Err(); err != nil {
return err // honor context cancellation
}
Expand Down Expand Up @@ -383,3 +377,10 @@ func decodeText(input, charset string) (string, error) {
}

var zipHeader = []byte("PK\x03\x04") // NOTE: headers of empty zip files might end with 0x05,0x06 or 0x06,0x06 instead of 0x03,0x04

// Interface guards
var (
_ Archiver = Zip{}
_ ArchiverAsync = Zip{}
_ Extractor = Zip{}
)

0 comments on commit fedac4a

Please sign in to comment.