From 84961abdf9fa3852a1dc2e8cbfac24db3b36f265 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sat, 10 Jun 2023 16:58:44 +0200 Subject: [PATCH] Add file.FSStack implementation Signed-off-by: Knut Ahlers --- file/fsstack.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 file/fsstack.go diff --git a/file/fsstack.go b/file/fsstack.go new file mode 100644 index 0000000..f2ca9e9 --- /dev/null +++ b/file/fsstack.go @@ -0,0 +1,48 @@ +package file + +import ( + "io" + "io/fs" + + "github.com/pkg/errors" +) + +// FSStack represents layers of fs.FS to open a file from. The first +// layer (starting at index 0, going up) responding other than +// fs.ErrNotExist will determine the response of this stack. +type FSStack []fs.FS + +var _ fs.FS = FSStack{} + +// Open iterates the FSStack starting at index 0, going up and returns +// the first non fs.ErrNotExist response. If all layers responds with +// fs.ErrNotExist a fs.PathError wrapping fs.ErrNotExist is returned. +func (f FSStack) Open(name string) (fs.File, error) { + for i, fse := range f { + file, err := fse.Open(name) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + continue + } + + return nil, errors.Wrapf(err, "opening file from layer %d", i) + } + + return file, nil + } + + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} +} + +// ReadFile is a convenice wrapper around Open and returns the content +// of the file if any is available. +func (f FSStack) ReadFile(name string) ([]byte, error) { + file, err := f.Open(name) + if err != nil { + return nil, errors.Wrap(err, "opening file") + } + defer file.Close() + + content, err := io.ReadAll(file) + return content, errors.Wrap(err, "reading content") +}