| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package fs provides a backend-agnostic filesystem layer for storing | |
| // performance results. | |
| package fs | |
| import ( | |
| "context" | |
| "errors" | |
| "io" | |
| "sort" | |
| "sync" | |
| ) | |
| // An FS stores uploaded benchmark data files. | |
| type FS interface { | |
| // NewWriter returns a Writer for a given file name. | |
| // When the Writer is closed, the file will be stored with the | |
| // given metadata and the data written to the writer. | |
| NewWriter(ctx context.Context, name string, metadata map[string]string) (Writer, error) | |
| } | |
| // A Writer is an io.Writer that can also be closed with an error. | |
| type Writer interface { | |
| io.WriteCloser | |
| // CloseWithError cancels the writing of the file, removing | |
| // any partially written data. | |
| CloseWithError(error) error | |
| } | |
| // MemFS is an in-memory filesystem implementing the FS interface. | |
| type MemFS struct { | |
| mu sync.Mutex | |
| content map[string]*memFile | |
| } | |
| // NewMemFS constructs a new, empty MemFS. | |
| func NewMemFS() *MemFS { | |
| return &MemFS{ | |
| content: make(map[string]*memFile), | |
| } | |
| } | |
| // NewWriter returns a Writer for a given file name. As a side effect, | |
| // it associates the given metadata with the file. | |
| func (fs *MemFS) NewWriter(_ context.Context, name string, metadata map[string]string) (Writer, error) { | |
| meta := make(map[string]string) | |
| for k, v := range metadata { | |
| meta[k] = v | |
| } | |
| return &memFile{fs: fs, name: name, metadata: meta}, nil | |
| } | |
| // Files returns the names of the files written to fs. | |
| func (fs *MemFS) Files() []string { | |
| fs.mu.Lock() | |
| defer fs.mu.Unlock() | |
| var files []string | |
| for f := range fs.content { | |
| files = append(files, f) | |
| } | |
| sort.Strings(files) | |
| return files | |
| } | |
| // memFile represents a file in a MemFS. While the file is being | |
| // written, fs points to the filesystem. Close writes the file's | |
| // content to fs and sets fs to nil. | |
| type memFile struct { | |
| fs *MemFS | |
| name string | |
| metadata map[string]string | |
| content []byte | |
| } | |
| func (f *memFile) Write(p []byte) (int, error) { | |
| f.content = append(f.content, p...) | |
| return len(p), nil | |
| } | |
| func (f *memFile) Close() error { | |
| if f.fs == nil { | |
| return errors.New("already closed") | |
| } | |
| f.fs.mu.Lock() | |
| defer f.fs.mu.Unlock() | |
| f.fs.content[f.name] = f | |
| f.fs = nil | |
| return nil | |
| } | |
| func (f *memFile) CloseWithError(error) error { | |
| f.fs = nil | |
| return nil | |
| } |