Initial QSfera import
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package fsx
|
||||
|
||||
import (
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
var (
|
||||
// assert interfaces implemented
|
||||
_ afero.Fs = (*FallbackFS)(nil)
|
||||
_ FS = (*FallbackFS)(nil)
|
||||
)
|
||||
|
||||
// FallbackFS is a filesystem that layers a primary filesystem on top of a secondary filesystem.
|
||||
type FallbackFS struct {
|
||||
FS
|
||||
primary *BaseFS
|
||||
secondary *BaseFS
|
||||
}
|
||||
|
||||
// Primary returns the primary filesystem.
|
||||
func (d *FallbackFS) Primary() *BaseFS {
|
||||
return d.primary
|
||||
}
|
||||
|
||||
// Secondary returns the secondary filesystem.
|
||||
func (d *FallbackFS) Secondary() *BaseFS {
|
||||
return d.secondary
|
||||
}
|
||||
|
||||
// NewFallbackFS returns a new FallbackFS instance.
|
||||
func NewFallbackFS(primary, secondary FS) *FallbackFS {
|
||||
return &FallbackFS{
|
||||
FS: FromAfero(afero.NewCopyOnWriteFs(secondary, primary)),
|
||||
primary: &BaseFS{Fs: primary},
|
||||
secondary: &BaseFS{Fs: secondary},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package fsx_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/qsfera/server/pkg/x/io/fsx"
|
||||
)
|
||||
|
||||
func TestLayeredFS(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
|
||||
read := func(fs fsx.FS, name string) (string, error) {
|
||||
f, err := fs.Open(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
mustRead := func(fs fsx.FS, name string) string {
|
||||
s, err := read(fs, name)
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
create := func(fs fsx.FS, name, content string) {
|
||||
err := afero.WriteFile(fs, name, []byte(content), 0644)
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
primary := fsx.NewMemMapFs()
|
||||
create(primary, "both.txt", "primary")
|
||||
g.Expect(mustRead(primary, "both.txt")).To(gomega.Equal("primary"))
|
||||
create(primary, "primary.txt", "primary")
|
||||
g.Expect(mustRead(primary, "primary.txt")).To(gomega.Equal("primary"))
|
||||
|
||||
secondary := fsx.NewMemMapFs()
|
||||
create(secondary, "both.txt", "secondary")
|
||||
g.Expect(mustRead(secondary, "both.txt")).To(gomega.Equal("secondary"))
|
||||
create(secondary, "secondary.txt", "secondary")
|
||||
g.Expect(mustRead(secondary, "secondary.txt")).To(gomega.Equal("secondary"))
|
||||
|
||||
fs := fsx.NewFallbackFS(primary, fsx.NewReadOnlyFs(secondary))
|
||||
g.Expect(mustRead(fs, "both.txt")).To(gomega.Equal("primary"))
|
||||
g.Expect(mustRead(fs, "primary.txt")).To(gomega.Equal("primary"))
|
||||
g.Expect(mustRead(fs, "secondary.txt")).To(gomega.Equal("secondary"))
|
||||
|
||||
create(fs, "fallback-fs.txt", "fallback-fs")
|
||||
g.Expect(mustRead(fs, "fallback-fs.txt")).To(gomega.Equal("fallback-fs"))
|
||||
g.Expect(mustRead(primary, "fallback-fs.txt")).To(gomega.Equal("fallback-fs"))
|
||||
g.Expect(mustRead(fs.Primary(), "fallback-fs.txt")).To(gomega.Equal("fallback-fs"))
|
||||
_, err := read(secondary, "fallback-fs.txt")
|
||||
g.Expect(err).To(gomega.HaveOccurred())
|
||||
_, err = read(fs.Secondary(), "fallback-fs.txt")
|
||||
g.Expect(err).To(gomega.HaveOccurred())
|
||||
}
|
||||
|
||||
func TestLayeredFS_Primary(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
primary := fsx.NewMemMapFs()
|
||||
fs := fsx.NewFallbackFS(primary, fsx.NewMemMapFs())
|
||||
|
||||
g.Expect(primary).To(gomega.BeIdenticalTo(fs.Primary().Fs))
|
||||
}
|
||||
|
||||
func TestLayeredFS_Secondary(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
secondary := fsx.NewMemMapFs()
|
||||
fs := fsx.NewFallbackFS(fsx.NewMemMapFs(), secondary)
|
||||
|
||||
g.Expect(secondary).To(gomega.BeIdenticalTo(fs.Secondary().Fs))
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package fsx
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
var (
|
||||
// assert interfaces implemented
|
||||
_ afero.Fs = (*BaseFS)(nil)
|
||||
_ FS = (*BaseFS)(nil)
|
||||
)
|
||||
|
||||
// FS is our default interface for filesystems.
|
||||
type FS interface {
|
||||
afero.Fs
|
||||
IOFS() fs.FS
|
||||
}
|
||||
|
||||
// BaseFS is our default implementation of the FS interface.
|
||||
type BaseFS struct {
|
||||
afero.Fs
|
||||
}
|
||||
|
||||
// IOFS returns the filesystem as an io/fs.FS.
|
||||
func (b *BaseFS) IOFS() fs.FS {
|
||||
return afero.NewIOFS(b)
|
||||
}
|
||||
|
||||
// FromAfero returns a new BaseFS instance from an afero.Fs.
|
||||
func FromAfero(fSys afero.Fs) *BaseFS {
|
||||
return &BaseFS{Fs: fSys}
|
||||
}
|
||||
|
||||
// FromIOFS returns a new BaseFS instance from an io/fs.FS.
|
||||
func FromIOFS(fSys fs.FS) *BaseFS {
|
||||
return FromAfero(&afero.FromIOFS{FS: fSys})
|
||||
}
|
||||
|
||||
// NewBasePathFs returns a new BaseFS which wraps the given filesystem with a base path.
|
||||
func NewBasePathFs(fSys FS, basePath string) *BaseFS {
|
||||
return FromAfero(afero.NewBasePathFs(fSys, basePath))
|
||||
}
|
||||
|
||||
// NewOsFs returns a new BaseFS which wraps the OS filesystem.
|
||||
func NewOsFs() *BaseFS {
|
||||
return FromAfero(afero.NewOsFs())
|
||||
}
|
||||
|
||||
// NewReadOnlyFs returns a new BaseFS which wraps the given filesystem with a read-only filesystem.
|
||||
func NewReadOnlyFs(FfSys FS) *BaseFS {
|
||||
return FromAfero(afero.NewReadOnlyFs(FfSys))
|
||||
}
|
||||
|
||||
// NewMemMapFs returns a new BaseFS which wraps the memory filesystem.
|
||||
func NewMemMapFs() *BaseFS {
|
||||
return FromAfero(afero.NewMemMapFs())
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package fsx_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
"github.com/qsfera/server/pkg/x/io/fsx"
|
||||
)
|
||||
|
||||
func TestBase(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
wrapped := fsx.NewMemMapFs()
|
||||
fs := fsx.BaseFS{Fs: wrapped.Fs}
|
||||
|
||||
g.Expect(wrapped.Fs).Should(gomega.BeIdenticalTo(fs.Fs))
|
||||
}
|
||||
|
||||
func TestBase_IOFS(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
fs := fsx.BaseFS{Fs: fsx.NewMemMapFs()}
|
||||
g.Expect(reflect.TypeOf(fs.IOFS()).Name()).Should(gomega.Equal("IOFS"))
|
||||
}
|
||||
|
||||
func TestFromIOFS(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
fs := fsx.FromIOFS(os.DirFS("."))
|
||||
|
||||
g.Expect(reflect.TypeOf(fs.Fs).String()).Should(gomega.Equal("*afero.FromIOFS"))
|
||||
}
|
||||
|
||||
func TestNewOsFs(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
fs := fsx.NewOsFs()
|
||||
|
||||
g.Expect(reflect.TypeOf(fs.Fs).String()).Should(gomega.Equal("*afero.OsFs"))
|
||||
}
|
||||
|
||||
func TestNewBasePathFs(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
base := fsx.NewMemMapFs()
|
||||
|
||||
err := base.MkdirAll("first/foo/bar", 0755)
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
err = base.MkdirAll("second/foo/baz", 0755)
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
fs := fsx.NewBasePathFs(base, "first")
|
||||
|
||||
info, err := fs.Stat("/foo/bar")
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
g.Expect(info.IsDir()).To(gomega.BeTrue())
|
||||
|
||||
info, err = fs.Stat("/foo/baz")
|
||||
g.Expect(err).To(gomega.HaveOccurred())
|
||||
g.Expect(info).To(gomega.BeNil())
|
||||
|
||||
info, err = fs.Stat("../second/foo/baz")
|
||||
g.Expect(err).To(gomega.HaveOccurred())
|
||||
g.Expect(info).To(gomega.BeNil())
|
||||
}
|
||||
|
||||
func TestNewReadOnlyFs(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
base := fsx.NewMemMapFs()
|
||||
|
||||
err := base.MkdirAll("first/foo/bar", 0755)
|
||||
g.Expect(err).ToNot(gomega.HaveOccurred())
|
||||
|
||||
fs := fsx.NewReadOnlyFs(base)
|
||||
err = fs.MkdirAll("first/foo/bay", 0755)
|
||||
g.Expect(err).To(gomega.HaveOccurred())
|
||||
}
|
||||
Reference in New Issue
Block a user