mirror of
https://github.com/1f349/overlapfs.git
synced 2024-11-13 23:41:38 +00:00
Finish writing tests
This commit is contained in:
parent
3bdbd3be80
commit
35ae69993c
1
a/a-test.txt
Normal file
1
a/a-test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Nothing
|
1
a/example.md
Normal file
1
a/example.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# This is an example
|
@ -1 +1 @@
|
|||||||
Empty
|
Empty
|
||||||
|
@ -1 +1 @@
|
|||||||
A
|
A
|
||||||
|
@ -1 +1 @@
|
|||||||
Hello World!
|
Hello World!
|
||||||
|
@ -1 +1 @@
|
|||||||
B
|
B
|
||||||
|
39
overlapfs.go
39
overlapfs.go
@ -2,15 +2,16 @@ package overlapfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OverlapFS layers B on top of A
|
// OverlapFS layers B on top of A
|
||||||
//
|
//
|
||||||
// Open and Stat reimplemented to show B unless ErrNotExist is returned then show 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.
|
// Glob and ReadDir are reimplemented to uniquely merge and sort the returned slices
|
||||||
//
|
//
|
||||||
// For ReadFile and Sub use the standard fs.ReadFile and fs.Sub implementations
|
// For ReadFile and Sub use the standard fs.ReadFile and fs.Sub implementations
|
||||||
type OverlapFS struct {
|
type OverlapFS struct {
|
||||||
@ -24,8 +25,9 @@ var _ interface {
|
|||||||
fs.ReadDirFS
|
fs.ReadDirFS
|
||||||
} = &OverlapFS{}
|
} = &OverlapFS{}
|
||||||
|
|
||||||
|
// Open implements fs.FS, the file named will be read from B if the file exists,
|
||||||
|
// otherwise the file named will be read from A
|
||||||
func (o OverlapFS) Open(name string) (fs.File, error) {
|
func (o OverlapFS) Open(name string) (fs.File, error) {
|
||||||
fmt.Println("Open", name)
|
|
||||||
open, err := o.B.Open(name)
|
open, err := o.B.Open(name)
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
@ -36,6 +38,8 @@ func (o OverlapFS) Open(name string) (fs.File, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stat implements fs.StatFS, the file named will have stats read from B if the
|
||||||
|
// file exists, otherwise the file named will have stats read from A
|
||||||
func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
|
func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
|
||||||
stat, err := fs.Stat(o.B, name)
|
stat, err := fs.Stat(o.B, name)
|
||||||
switch {
|
switch {
|
||||||
@ -47,36 +51,39 @@ func (o OverlapFS) Stat(name string) (fs.FileInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Glob implements fs.GlobFS, the pattern will be globed in B and A and the
|
||||||
|
// result uniquely merged and sorted
|
||||||
func (o OverlapFS) Glob(pattern string) ([]string, error) {
|
func (o OverlapFS) Glob(pattern string) ([]string, error) {
|
||||||
aSlice, err := fs.Glob(o.B, pattern)
|
bSlice, err := fs.Glob(o.B, pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bSlice, err := fs.Glob(o.A, pattern)
|
aSlice, err := fs.Glob(o.A, pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeUnique(aSlice, bSlice, func(s string) string { return s })
|
return mergeUnique(aSlice, bSlice, func(s string) string { return s }), nil
|
||||||
return aSlice, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadDir implements fs.ReadDirFS, the directory named will be read in B and A
|
||||||
|
// and the result uniquely merged and sorted
|
||||||
func (o OverlapFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
func (o OverlapFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||||
aSlice, err := fs.ReadDir(o.B, name)
|
bSlice, err := fs.ReadDir(o.B, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bSlice, err := fs.ReadDir(o.A, name)
|
aSlice, err := fs.ReadDir(o.A, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeUnique(aSlice, bSlice, func(entry fs.DirEntry) string { return entry.Name() })
|
return mergeUnique(aSlice, bSlice, func(entry fs.DirEntry) string { return entry.Name() }), nil
|
||||||
return aSlice, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeUnique puts unique values from b into a
|
// mergeUnique puts unique values from b into a and sorts the resulting merged
|
||||||
func mergeUnique[T any](a, b []T, key func(T) string) {
|
// slice
|
||||||
|
func mergeUnique[T any](a, b []T, key func(T) string) []T {
|
||||||
m := make(map[string]struct{})
|
m := make(map[string]struct{})
|
||||||
for _, i := range a {
|
for _, i := range a {
|
||||||
m[key(i)] = struct{}{}
|
m[key(i)] = struct{}{}
|
||||||
@ -86,4 +93,8 @@ func mergeUnique[T any](a, b []T, key func(T) string) {
|
|||||||
a = append(a, i)
|
a = append(a, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slices.SortFunc(a, func(a, b T) int {
|
||||||
|
return strings.Compare(key(a), key(b))
|
||||||
|
})
|
||||||
|
return a
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,32 @@ func TestOverlapFS_Open(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "hello.txt", stat.Name())
|
assert.Equal(t, "hello.txt", stat.Name())
|
||||||
bAll, err := io.ReadAll(open)
|
bAll, err := io.ReadAll(open)
|
||||||
assert.Equal(t, "Hello World!", string(bAll))
|
assert.Equal(t, "Hello World!\n", string(bAll))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOverlapFS_Stat(t *testing.T) {
|
func TestOverlapFS_Stat(t *testing.T) {
|
||||||
o := OverlapFS{A: a.Embed, B: b.Embed}
|
o := OverlapFS{A: a.Embed, B: b.Embed}
|
||||||
open, err := o.Stat("hello.txt")
|
stat, err := o.Stat("hello.txt")
|
||||||
assert.NoError(t, err)
|
|
||||||
stat, err := open.Stat()
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "hello.txt", stat.Name())
|
assert.Equal(t, "hello.txt", stat.Name())
|
||||||
bAll, err := io.ReadAll(open)
|
assert.Equal(t, int64(13), stat.Size())
|
||||||
assert.Equal(t, "Hello World!", string(bAll))
|
assert.False(t, stat.IsDir())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverlapFS_Glob(t *testing.T) {
|
||||||
|
o := OverlapFS{A: a.Embed, B: b.Embed}
|
||||||
|
glob, err := o.Glob("*.txt")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []string{"a-test.txt", "hello.txt", "just-a.txt", "just-b.txt"}, glob)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverlapFS_ReadDir(t *testing.T) {
|
||||||
|
o := OverlapFS{A: a.Embed, B: b.Embed}
|
||||||
|
dir, err := o.ReadDir(".")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
z := make([]string, 0, len(dir))
|
||||||
|
for _, i := range dir {
|
||||||
|
z = append(z, i.Name())
|
||||||
|
}
|
||||||
|
assert.Equal(t, []string{"a-test.txt", "a.go", "b.go", "example.md", "hello.txt", "just-a.txt", "just-b.txt"}, z)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user