Quentin Smith | 6ceaac6 | 2016-12-21 16:13:49 -0500 | [diff] [blame] | 1 | // Copyright 2016 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // Package fs provides a backend-agnostic filesystem layer for storing |
| 6 | // performance results. |
| 7 | package fs |
| 8 | |
| 9 | import ( |
| 10 | "errors" |
| 11 | "io" |
| 12 | "sort" |
| 13 | "sync" |
| 14 | |
| 15 | "golang.org/x/net/context" |
| 16 | ) |
| 17 | |
| 18 | // An FS stores uploaded benchmark data files. |
| 19 | type FS interface { |
| 20 | // NewWriter returns a Writer for a given file name. |
| 21 | // When the Writer is closed, the file will be stored with the |
| 22 | // given metadata and the data written to the writer. |
| 23 | NewWriter(ctx context.Context, name string, metadata map[string]string) (Writer, error) |
| 24 | } |
| 25 | |
| 26 | // A Writer is an io.Writer that can also be closed with an error. |
| 27 | type Writer interface { |
| 28 | io.WriteCloser |
| 29 | // CloseWithError cancels the writing of the file, removing |
| 30 | // any partially written data. |
| 31 | CloseWithError(error) error |
| 32 | } |
| 33 | |
| 34 | // MemFS is an in-memory filesystem implementing the FS interface. |
| 35 | type MemFS struct { |
| 36 | mu sync.Mutex |
| 37 | content map[string]*memFile |
| 38 | } |
| 39 | |
| 40 | // NewMemFS constructs a new, empty MemFS. |
| 41 | func NewMemFS() *MemFS { |
| 42 | return &MemFS{ |
| 43 | content: make(map[string]*memFile), |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // NewWriter returns a Writer for a given file name. As a side effect, |
| 48 | // it associates the given metadata with the file. |
| 49 | func (fs *MemFS) NewWriter(_ context.Context, name string, metadata map[string]string) (Writer, error) { |
| 50 | meta := make(map[string]string) |
| 51 | for k, v := range metadata { |
| 52 | meta[k] = v |
| 53 | } |
| 54 | return &memFile{fs: fs, name: name, metadata: meta}, nil |
| 55 | } |
| 56 | |
| 57 | // Files returns the names of the files written to fs. |
| 58 | func (fs *MemFS) Files() []string { |
| 59 | fs.mu.Lock() |
| 60 | defer fs.mu.Unlock() |
| 61 | var files []string |
| 62 | for f := range fs.content { |
| 63 | files = append(files, f) |
| 64 | } |
| 65 | sort.Strings(files) |
| 66 | return files |
| 67 | } |
| 68 | |
| 69 | // memFile represents a file in a MemFS. While the file is being |
| 70 | // written, fs points to the filesystem. Close writes the file's |
| 71 | // content to fs and sets fs to nil. |
| 72 | type memFile struct { |
| 73 | fs *MemFS |
| 74 | name string |
| 75 | metadata map[string]string |
| 76 | content []byte |
| 77 | } |
| 78 | |
| 79 | func (f *memFile) Write(p []byte) (int, error) { |
| 80 | f.content = append(f.content, p...) |
| 81 | return len(p), nil |
| 82 | } |
| 83 | |
| 84 | func (f *memFile) Close() error { |
| 85 | if f.fs == nil { |
| 86 | return errors.New("already closed") |
| 87 | } |
| 88 | f.fs.mu.Lock() |
| 89 | defer f.fs.mu.Unlock() |
| 90 | f.fs.content[f.name] = f |
| 91 | f.fs = nil |
| 92 | return nil |
| 93 | } |
| 94 | |
| 95 | func (f *memFile) CloseWithError(error) error { |
| 96 | f.fs = nil |
| 97 | return nil |
| 98 | } |