mirror of
https://github.com/1f349/overlapfs.git
synced 2024-11-09 22:52:56 +00:00
Initial source code
This commit is contained in:
parent
5800326c1f
commit
3bdbd3be80
6
a/a.go
Normal file
6
a/a.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed *
|
||||||
|
var Embed embed.FS
|
1
a/hello.txt
Normal file
1
a/hello.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Empty
|
1
a/just-a.txt
Normal file
1
a/just-a.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
A
|
6
b/b.go
Normal file
6
b/b.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package b
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed *
|
||||||
|
var Embed embed.FS
|
1
b/hello.txt
Normal file
1
b/hello.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello World!
|
1
b/just-b.txt
Normal file
1
b/just-b.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
B
|
11
go.mod
Normal file
11
go.mod
Normal 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
10
go.sum
Normal 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
89
overlapfs.go
Normal 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
31
overlapfs_test.go
Normal 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))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user