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 }