...

Source file src/github.com/Shopify/go-storage/hash_wrapper.go

Documentation: github.com/Shopify/go-storage

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"hash"
     9  	"io"
    10  )
    11  
    12  // NewHashWrapper creates a content addressable filesystem using hash.Hash
    13  // to sum the content and store it using that name.
    14  func NewHashWrapper(h hash.Hash, fs FS, gs GetSetter) FS {
    15  	return &hashWrapper{
    16  		h:  h,
    17  		fs: fs,
    18  		gs: gs,
    19  	}
    20  }
    21  
    22  type hashWrapper struct {
    23  	h  hash.Hash
    24  	fs FS
    25  	gs GetSetter
    26  }
    27  
    28  // GetSetter implements a key-value store which is concurrency safe (can
    29  // be used in multiple go-routines concurrently).
    30  type GetSetter interface {
    31  	Get(key string) (string, error)
    32  	Set(key string, value string) error
    33  	Delete(key string) error
    34  }
    35  
    36  // Open implements FS.
    37  func (hfs *hashWrapper) Open(ctx context.Context, path string, options *ReaderOptions) (*File, error) {
    38  	v, err := hfs.gs.Get(path)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	return hfs.fs.Open(ctx, v, options)
    44  }
    45  
    46  // Attributes implements FS.
    47  func (hfs *hashWrapper) Attributes(ctx context.Context, path string, options *ReaderOptions) (*Attributes, error) {
    48  	v, err := hfs.gs.Get(path)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return hfs.fs.Attributes(ctx, v, options)
    54  }
    55  
    56  // Walk implements Walker.
    57  func (hfs *hashWrapper) Walk(_ context.Context, _ string, _ WalkFn) error {
    58  	return errors.New("HashFS.Walk is not implemented")
    59  }
    60  
    61  type hashWriteCloser struct {
    62  	buf  *bytes.Buffer
    63  	path string
    64  	ctx  context.Context
    65  
    66  	hfs     *hashWrapper
    67  	options *WriterOptions
    68  }
    69  
    70  func (w *hashWriteCloser) Write(b []byte) (int, error) {
    71  	n, err := w.buf.Write(b)
    72  	if err != nil {
    73  		return n, err
    74  	}
    75  
    76  	return w.hfs.h.Write(b) // never returns an error
    77  }
    78  
    79  func (w *hashWriteCloser) Close() (err error) {
    80  	hashPath := fmt.Sprintf("%x", w.hfs.h.Sum(nil))
    81  	if err := w.hfs.gs.Set(w.path, hashPath); err != nil {
    82  		return err
    83  	}
    84  
    85  	fsw, err := w.hfs.fs.Create(w.ctx, hashPath, w.options)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer func() {
    90  		if err1 := fsw.Close(); err == nil {
    91  			err = err1
    92  		}
    93  	}()
    94  
    95  	_, err = io.Copy(fsw, w.buf)
    96  
    97  	return err
    98  }
    99  
   100  // TODO(trent): make sure that you document the FS.Create method
   101  // to Close it, and check the error. If err != nil then the file might not have
   102  // been written.
   103  func (hfs *hashWrapper) Create(ctx context.Context, path string, options *WriterOptions) (io.WriteCloser, error) {
   104  	return &hashWriteCloser{
   105  		buf:     &bytes.Buffer{},
   106  		path:    path,
   107  		ctx:     ctx,
   108  		hfs:     hfs,
   109  		options: options,
   110  	}, nil
   111  }
   112  
   113  // Delete implements FS.
   114  func (hfs *hashWrapper) Delete(ctx context.Context, path string) error {
   115  	if err := hfs.fs.Delete(ctx, path); err != nil {
   116  		return err
   117  	}
   118  
   119  	return hfs.gs.Delete(path)
   120  }
   121  
   122  func (hfs *hashWrapper) URL(ctx context.Context, path string, options *SignedURLOptions) (string, error) {
   123  	// Pass-through
   124  	return hfs.fs.URL(ctx, path, options)
   125  }
   126  

View as plain text