github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/file/file.go (about)

     1  // Copyright 2018 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package file
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/Schaudge/grailbase/errors"
    13  	"github.com/Schaudge/grailbase/ioctx"
    14  )
    15  
    16  // File defines operations on a file. Implementations must be thread safe.
    17  type File interface {
    18  	// String returns a diagnostic string.
    19  	String() string
    20  
    21  	// Name returns the path name given to file.Open or file.Create when this
    22  	// object was created.
    23  	Name() string
    24  
    25  	// Stat returns file metadata.
    26  	//
    27  	// REQUIRES: Close has not been called
    28  	Stat(ctx context.Context) (Info, error)
    29  
    30  	// Reader creates an io.ReadSeeker object that operates on the file.  If
    31  	// Reader() is called multiple times, they share the seek pointer.
    32  	//
    33  	// For emphasis: these share state, which is different from OffsetReader!
    34  	//
    35  	// REQUIRES: Close has not been called
    36  	Reader(ctx context.Context) io.ReadSeeker
    37  
    38  	// OffsetReader creates a new, independent ioctx.ReadCloser, starting at
    39  	// offset. Unlike Reader, its position in the file is only modified by Read
    40  	// on this object. The returned object is not thread-safe, and callers are
    41  	// responsible for serializing all of their calls, including calling Close
    42  	// after all Reads are done. Of course, callers can use separate
    43  	// OffsetReaders in parallel.
    44  	//
    45  	// Background: This API reflects S3's performance characteristics, where
    46  	// initiating a new read position is relatively expensive, but then
    47  	// streaming data is fast (including in parallel with multiple readers).
    48  	//
    49  	// REQUIRES: Close has not been called
    50  	OffsetReader(offset int64) ioctx.ReadCloser
    51  
    52  	// Writer creates a writes that to the file. If Writer() is called multiple
    53  	// times, they share the seek pointer.
    54  	//
    55  	// REQUIRES: Close has not been called
    56  	Writer(ctx context.Context) io.Writer
    57  
    58  	// TODO: Introduce WriterAt() ioctx.WriterAt, analogous to ReaderAt.
    59  
    60  	// Discard discards a file before it is closed, relinquishing any
    61  	// temporary resources implied by pending writes. This should be
    62  	// used if the caller decides not to complete writing the file.
    63  	// Discard is a best-effort operation. Discard is not defined for
    64  	// files opened for reading. Exactly one of Discard or Close should
    65  	// be called. No other File, io.ReadSeeker, or io.Writer methods
    66  	// shall be called after Discard.
    67  	Discard(ctx context.Context)
    68  
    69  	// Closer commits the contents of a written file, invalidating the
    70  	// File and all Readers and Writers created from the file. Exactly
    71  	// one of Discard or Close should be called. No other File or
    72  	// io.ReadSeeker, io.Writer methods shall be called after Close.
    73  	Closer
    74  }
    75  
    76  // TODO: Migrate callers to use new location.
    77  type Closer = ioctx.Closer
    78  
    79  // ETagged defines a getter for a file with an ETag.
    80  type ETagged interface {
    81  	// ETag is an identifier assigned to a specific version of the file.
    82  	ETag() string
    83  }
    84  
    85  // CloseAndReport returns a defer-able helper that calls f.Close and reports errors, if any,
    86  // to *err. Pass your function's named return error. Example usage:
    87  //
    88  //   func processFile(filename string) (_ int, err error) {
    89  //     ctx := context.Background()
    90  //     f, err := file.Open(ctx, filename)
    91  //     if err != nil { ... }
    92  //     defer file.CloseAndReport(ctx, f, &err)
    93  //     ...
    94  //   }
    95  //
    96  // If your function returns with an error, any f.Close error will be chained appropriately.
    97  //
    98  // Deprecated: Use errors.CleanUpCtx directly.
    99  func CloseAndReport(ctx context.Context, f Closer, err *error) {
   100  	errors.CleanUpCtx(ctx, f.Close, err)
   101  }
   102  
   103  // MustClose is a defer-able function that calls f.Close and panics on error.
   104  //
   105  // Example:
   106  //   ctx := context.Background()
   107  //   f, err := file.Open(ctx, filename)
   108  //   if err != nil { panic(err) }
   109  //   defer file.MustClose(ctx, f)
   110  //   ...
   111  func MustClose(ctx context.Context, f Closer) {
   112  	if err := f.Close(ctx); err != nil {
   113  		if n, ok := f.(named); ok {
   114  			panic(fmt.Sprintf("close %s: %v", n.Name(), err))
   115  		}
   116  		panic(err)
   117  	}
   118  }
   119  
   120  type named interface {
   121  	// Name returns the path name given to file.Open or file.Create when this
   122  	// object was created.
   123  	Name() string
   124  }
   125  
   126  // Error implements io.{Reader,Writer,Seeker,Closer}. It returns the given error
   127  // to any call.
   128  type Error struct{ err error }
   129  
   130  // NewError returns a new Error object that returns the given error to any
   131  // Read/Write/Seek/Close call.
   132  func NewError(err error) *Error { return &Error{err: err} }
   133  
   134  // Read implements io.Reader
   135  func (r *Error) Read([]byte) (int, error) {
   136  	return -1, r.err
   137  }
   138  
   139  // Seek implements io.Seeker.
   140  func (r *Error) Seek(int64, int) (int64, error) {
   141  	return -1, r.err
   142  }
   143  
   144  // Write implements io.Writer.
   145  func (r *Error) Write([]byte) (int, error) {
   146  	return -1, r.err
   147  }
   148  
   149  // Close implements io.Closer.
   150  func (r *Error) Close() error {
   151  	return r.err
   152  }