-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interface->fs.FS translation logic #8
base: main
Are you sure you want to change the base?
Changes from 6 commits
f05962d
36ae907
aad3981
7f96f7b
58ec331
30f8c03
3c22efa
be29704
6935a52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package simpleblob | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"io/fs" | ||
"time" | ||
) | ||
|
||
// fsInterfaceWrapper wraps an Interface and implements fs.FS. | ||
type fsInterfaceWrapper struct{ Interface } | ||
|
||
// fsBlobWrapper represents data upstream and implements both fs.File | ||
// and fs.FileInfo for convenience. | ||
type fsBlobWrapper struct { | ||
b *Blob | ||
parent *fsInterfaceWrapper | ||
r *bytes.Reader | ||
} | ||
|
||
// AsFS casts the provided interface to a fs.FS interface if supported, | ||
// else it wraps it to replicate its functionalities. | ||
func AsFS(st Interface) fs.FS { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since all storage methods require a context and the FS interface itself does have have a way to provide one, I suggest passing it here and holding on to it in the wrapper. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, I'll push a fix. Thank you! |
||
if fsys, ok := st.(fs.FS); ok { | ||
return fsys | ||
} | ||
return &fsInterfaceWrapper{st} | ||
} | ||
|
||
// Open retrieves a Blob, wrapped as a fs.File, from the underlying Interface. | ||
func (stw *fsInterfaceWrapper) Open(name string) (fs.File, error) { | ||
b, err := stw.Load(context.Background(), name) | ||
if err != nil { | ||
return nil, &fs.PathError{Op: "open", Path: name, Err: err} | ||
} | ||
return &fsBlobWrapper{&Blob{name, int64(len(b))}, stw, nil}, nil | ||
} | ||
|
||
// ReadFile implements fs.ReadFileFS on top of an Interface wrapped as a fs.FS. | ||
func (stw *fsInterfaceWrapper) ReadFile(name string) ([]byte, error) { | ||
return stw.Load(context.Background(), name) | ||
} | ||
|
||
// fs.FileInfo implementation | ||
|
||
func (*fsBlobWrapper) IsDir() bool { return false } | ||
func (*fsBlobWrapper) ModTime() time.Time { return time.Time{} } | ||
func (*fsBlobWrapper) Mode() fs.FileMode { return 0777 } | ||
func (bw *fsBlobWrapper) Name() string { return bw.b.Name } | ||
func (bw *fsBlobWrapper) Sys() interface{} { return bw.parent } | ||
func (bw *fsBlobWrapper) Size() int64 { return bw.b.Size } | ||
|
||
// fs.File implementation | ||
|
||
func (bw *fsBlobWrapper) Stat() (fs.FileInfo, error) { | ||
return bw, nil | ||
} | ||
func (bw *fsBlobWrapper) Read(p []byte) (int, error) { | ||
if bw.r == nil { | ||
b, err := bw.parent.Interface.Load(context.Background(), bw.b.Name) | ||
if err != nil { | ||
return 0, err | ||
} | ||
bw.r = bytes.NewReader(b) | ||
} | ||
return bw.r.Read(p) | ||
} | ||
func (bw *fsBlobWrapper) Close() error { | ||
if bw.r != nil { | ||
bw.r = nil | ||
} | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would fold this into
DoBackendTests
, so that that one always tests everything we would want to test about a backend. It could skip certain tests if the backend does not implement an optional backend.