Initial source code

This commit is contained in:
Melon 2023-10-08 15:32:36 +01:00
parent 5800326c1f
commit 3bdbd3be80
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
10 changed files with 157 additions and 0 deletions

6
a/a.go Normal file
View File

@ -0,0 +1,6 @@
package a
import "embed"
//go:embed *
var Embed embed.FS

1
a/hello.txt Normal file
View File

@ -0,0 +1 @@
Empty

1
a/just-a.txt Normal file
View File

@ -0,0 +1 @@
A

6
b/b.go Normal file
View File

@ -0,0 +1,6 @@
package b
import "embed"
//go:embed *
var Embed embed.FS

1
b/hello.txt Normal file
View File

@ -0,0 +1 @@
Hello World!

1
b/just-b.txt Normal file
View File

@ -0,0 +1 @@
B

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module github.com/1f349/overlapfs
go 1.21.1
require github.com/stretchr/testify v1.8.4
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

10
go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

89
overlapfs.go Normal file
View File

@ -0,0 +1,89 @@
package overlapfs
import (
"errors"
"fmt"
"io/fs"
)
// OverlapFS layers B on top of A
//
// Open and Stat reimplemented to show B unless ErrNotExist is returned then show A.
//
// Glob and ReadDir are reimplemented to uniquely merge the returned lists.
//
// For ReadFile and Sub use the standard fs.ReadFile and fs.Sub implementations
type OverlapFS struct {
A, B fs.FS
}
var _ interface {
fs.FS
fs.StatFS
fs.GlobFS
fs.ReadDirFS
} = &OverlapFS{}
func (o OverlapFS) Open(name string) (fs.File, error) {
fmt.Println("Open", name)
open, err := o.B.Open(name)
switch {
case err == nil:
return open, nil
case errors.Is(err, fs.ErrNotExist):
return o.A.Open(name)
}
return nil, err
}
func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
stat, err := fs.Stat(o.B, name)
switch {
case err == nil:
return stat, nil
case errors.Is(err, fs.ErrNotExist):
return fs.Stat(o.A, name)
}
return nil, err
}
func (o OverlapFS) Glob(pattern string) ([]string, error) {
aSlice, err := fs.Glob(o.B, pattern)
if err != nil {
return nil, err
}
bSlice, err := fs.Glob(o.A, pattern)
if err != nil {
return nil, err
}
mergeUnique(aSlice, bSlice, func(s string) string { return s })
return aSlice, nil
}
func (o OverlapFS) ReadDir(name string) ([]fs.DirEntry, error) {
aSlice, err := fs.ReadDir(o.B, name)
if err != nil {
return nil, err
}
bSlice, err := fs.ReadDir(o.A, name)
if err != nil {
return nil, err
}
mergeUnique(aSlice, bSlice, func(entry fs.DirEntry) string { return entry.Name() })
return aSlice, nil
}
// mergeUnique puts unique values from b into a
func mergeUnique[T any](a, b []T, key func(T) string) {
m := make(map[string]struct{})
for _, i := range a {
m[key(i)] = struct{}{}
}
for _, i := range b {
if _, ok := m[key(i)]; !ok {
a = append(a, i)
}
}
}

31
overlapfs_test.go Normal file
View File

@ -0,0 +1,31 @@
package overlapfs
import (
"github.com/1f349/overlapfs/a"
"github.com/1f349/overlapfs/b"
"github.com/stretchr/testify/assert"
"io"
"testing"
)
func TestOverlapFS_Open(t *testing.T) {
o := OverlapFS{A: a.Embed, B: b.Embed}
open, err := o.Open("hello.txt")
assert.NoError(t, err)
stat, err := open.Stat()
assert.NoError(t, err)
assert.Equal(t, "hello.txt", stat.Name())
bAll, err := io.ReadAll(open)
assert.Equal(t, "Hello World!", string(bAll))
}
func TestOverlapFS_Stat(t *testing.T) {
o := OverlapFS{A: a.Embed, B: b.Embed}
open, err := o.Stat("hello.txt")
assert.NoError(t, err)
stat, err := open.Stat()
assert.NoError(t, err)
assert.Equal(t, "hello.txt", stat.Name())
bAll, err := io.ReadAll(open)
assert.Equal(t, "Hello World!", string(bAll))
}