whatcanGOwrong
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
file 1 content from dir 1
|
||||
+1
@@ -0,0 +1 @@
|
||||
file 1 content from dir 2
|
||||
+1
@@ -0,0 +1 @@
|
||||
file 2 content
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package unionfs allows multiple file systems to be read as a union.
|
||||
package unionfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
var _ fs.ReadDirFS = FS{}
|
||||
|
||||
// A FS is an FS presenting the union of the file systems in the slice. If
|
||||
// multiple file systems provide a particular file, Open uses the FS listed
|
||||
// earlier in the slice.
|
||||
type FS []fs.FS
|
||||
|
||||
// Sub returns an FS corresponding to the merged subtree rooted at a set of
|
||||
// fsys's dirs.
|
||||
func Sub(fsys fs.FS, dirs ...string) (FS, error) {
|
||||
var subs FS
|
||||
for _, dir := range dirs {
|
||||
if _, err := fs.Stat(fsys, dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := fs.Sub(fsys, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
return subs, nil
|
||||
}
|
||||
|
||||
func (fsys FS) Open(name string) (fs.File, error) {
|
||||
var errOut error
|
||||
for _, sub := range fsys {
|
||||
f, err := sub.Open(name)
|
||||
if err == nil {
|
||||
return f, nil
|
||||
}
|
||||
if errOut == nil {
|
||||
errOut = err
|
||||
}
|
||||
}
|
||||
return nil, errOut
|
||||
}
|
||||
|
||||
func (fsys FS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
var all []fs.DirEntry
|
||||
var seen map[string]bool // seen[name] is true if name is listed in all; lazily initialized
|
||||
var errOut error
|
||||
for _, sub := range fsys {
|
||||
list, err := fs.ReadDir(sub, name)
|
||||
if err != nil {
|
||||
errOut = err
|
||||
}
|
||||
if len(all) == 0 {
|
||||
all = append(all, list...)
|
||||
} else {
|
||||
if seen == nil {
|
||||
// Initialize seen only after we get two different directory listings.
|
||||
seen = make(map[string]bool)
|
||||
for _, d := range all {
|
||||
seen[d.Name()] = true
|
||||
}
|
||||
}
|
||||
for _, d := range list {
|
||||
name := d.Name()
|
||||
if !seen[name] {
|
||||
seen[name] = true
|
||||
all = append(all, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(all) > 0 {
|
||||
return all, nil
|
||||
}
|
||||
return nil, errOut
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package unionfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFS_Open(t *testing.T) {
|
||||
fsys, err := Sub(os.DirFS("testdata"), "dir1", "dir2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "file1 from dir1",
|
||||
args: args{
|
||||
name: "file1",
|
||||
},
|
||||
want: "file 1 content from dir 1\n",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "file2 from dir2",
|
||||
args: args{
|
||||
name: "file2",
|
||||
},
|
||||
want: "file 2 content\n",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "file not found",
|
||||
args: args{
|
||||
name: "file3",
|
||||
},
|
||||
want: "file3",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
file, err := fsys.Open(tt.args.name)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("FS.Open() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr {
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := string(bytes)
|
||||
if got != tt.want {
|
||||
t.Errorf("FS.Open() = %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFS_ReadDir(t *testing.T) {
|
||||
var err error
|
||||
fsys, err := Sub(os.DirFS("testdata"), "dir1", "dir2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
fsys FS
|
||||
wantFiles []string
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
args: args{"."},
|
||||
fsys: fsys,
|
||||
wantFiles: []string{"file1", "file2"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dirs, err := tt.fsys.ReadDir(tt.args.name)
|
||||
if err != nil {
|
||||
t.Errorf("FS.ReadDir() error = %v", err)
|
||||
return
|
||||
}
|
||||
var got []string
|
||||
for _, v := range dirs {
|
||||
got = append(got, v.Name())
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.wantFiles) {
|
||||
t.Errorf("FS.ReadDir() = %v, want %v", got, tt.wantFiles)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user