forked from mirror/afero
Add RegexpFilter
- rework FilterFs to be truely chaining, i.e. every call not intercepted must be passed to the source Fs - AddFilter accepts just a FilterFs, not a Fs - FilterFs' AddFilter(FilterFs) should be implemented by any FilterFs like func (f *myFilter) AddFilter(fs FilterFs) { fs.SetSource(f.source) f.source = fs }
This commit is contained in:
parent
98ed0a3409
commit
f4b37b446e
103
filter.go
103
filter.go
|
@ -15,14 +15,18 @@ import (
|
|||
// of filtering (e.g. admins get write access, normal users just readonly)
|
||||
type FilterFs interface {
|
||||
Fs
|
||||
AddFilter(Fs)
|
||||
AddFilter(FilterFs)
|
||||
SetSource(Fs)
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
chain []Fs
|
||||
source Fs
|
||||
}
|
||||
|
||||
func (f *Filter) SetSource(fs Fs) {
|
||||
f.source = fs
|
||||
}
|
||||
|
||||
// create a new FilterFs that implements Fs, argument must be an Fs, not
|
||||
// a FilterFs
|
||||
func NewFilter(fs Fs) FilterFs {
|
||||
|
@ -30,101 +34,44 @@ func NewFilter(fs Fs) FilterFs {
|
|||
}
|
||||
|
||||
// prepend a filter in the filter chain
|
||||
func (f *Filter) AddFilter(fs Fs) {
|
||||
c := []Fs{fs}
|
||||
for _, ch := range f.chain {
|
||||
c = append(c, ch)
|
||||
}
|
||||
f.chain = c
|
||||
func (f *Filter) AddFilter(fs FilterFs) {
|
||||
fs.SetSource(f.source)
|
||||
f.source = fs
|
||||
}
|
||||
|
||||
func (f *Filter) Create(name string) (file File, err error) {
|
||||
for _, c := range f.chain {
|
||||
file, err = c.Create(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return f.source.Create(name)
|
||||
}
|
||||
|
||||
func (f *Filter) Mkdir(name string, perm os.FileMode) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.Mkdir(name, perm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Mkdir(name string, perm os.FileMode) (error) {
|
||||
return f.source.Mkdir(name, perm)
|
||||
}
|
||||
|
||||
func (f *Filter) MkdirAll(path string, perm os.FileMode) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.MkdirAll(path, perm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) MkdirAll(path string, perm os.FileMode) (error) {
|
||||
return f.source.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
func (f *Filter) Open(name string) (file File, err error) {
|
||||
for _, c := range f.chain {
|
||||
file, err = c.Open(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Open(name string) (File, error) {
|
||||
return f.source.Open(name)
|
||||
}
|
||||
|
||||
func (f *Filter) OpenFile(name string, flag int, perm os.FileMode) (file File, err error) {
|
||||
for _, c := range f.chain {
|
||||
file, err = c.OpenFile(name, flag, perm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
return f.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (f *Filter) Remove(name string) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.Remove(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Remove(name string) (error) {
|
||||
return f.source.Remove(name)
|
||||
}
|
||||
|
||||
func (f *Filter) RemoveAll(path string) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.RemoveAll(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) RemoveAll(path string) (error) {
|
||||
return f.source.RemoveAll(path)
|
||||
}
|
||||
|
||||
func (f *Filter) Rename(oldname, newname string) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.Rename(oldname, newname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Rename(oldname, newname string) (error) {
|
||||
return f.source.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (f *Filter) Stat(name string) (fi os.FileInfo, err error) {
|
||||
for _, c := range f.chain {
|
||||
fi, err = c.Stat(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Stat(name string) (os.FileInfo, error) {
|
||||
return f.source.Stat(name)
|
||||
}
|
||||
|
||||
|
@ -132,22 +79,10 @@ func (f *Filter) Name() string {
|
|||
return f.source.Name()
|
||||
}
|
||||
|
||||
func (f *Filter) Chmod(name string, mode os.FileMode) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.Chmod(name, mode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Chmod(name string, mode os.FileMode) (error) {
|
||||
return f.source.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (f *Filter) Chtimes(name string, atime, mtime time.Time) (err error) {
|
||||
for _, c := range f.chain {
|
||||
err = c.Chtimes(name, atime, mtime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
func (f *Filter) Chtimes(name string, atime, mtime time.Time) (error) {
|
||||
return f.source.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
|
|
@ -7,12 +7,27 @@ import (
|
|||
)
|
||||
|
||||
type ReadOnlyFilter struct {
|
||||
source Fs
|
||||
}
|
||||
|
||||
func NewReadonlyFilter() Fs {
|
||||
func NewReadonlyFilter() FilterFs {
|
||||
return &ReadOnlyFilter{}
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) SetSource(fs Fs) {
|
||||
r.source = fs
|
||||
}
|
||||
|
||||
// prepend a filter in the filter chain
|
||||
func (r *ReadOnlyFilter) AddFilter(fs FilterFs) {
|
||||
fs.SetSource(r.source)
|
||||
r.source = fs
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) ReadDir(name string) ([]os.FileInfo, error) {
|
||||
return ReadDir(r.source, name)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Chtimes(n string, a, m time.Time) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
@ -22,11 +37,11 @@ func (r *ReadOnlyFilter) Chmod(n string, m os.FileMode) error {
|
|||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Name() string {
|
||||
return "readOnlyFilter"
|
||||
return "ReadOnlyFilter"
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Stat(name string) (os.FileInfo, error) {
|
||||
return nil, nil
|
||||
return r.source.Stat(name)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Rename(o, n string) error {
|
||||
|
@ -45,11 +60,11 @@ func (r *ReadOnlyFilter) OpenFile(name string, flag int, perm os.FileMode) (File
|
|||
if flag&(os.O_WRONLY|syscall.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
|
||||
return nil, syscall.EPERM
|
||||
}
|
||||
return nil, nil
|
||||
return r.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Open(n string) (File, error) {
|
||||
return nil, nil
|
||||
return r.source.Open(n)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFilter) Mkdir(n string, p os.FileMode) error {
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
package afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The RegexpFilter filters files (not directories) by regular expression. Only
|
||||
// files matching the given regexp will be allowed, all others get a ENOENT error (
|
||||
// "No such file or directory").
|
||||
//
|
||||
type RegexpFilter struct {
|
||||
re *regexp.Regexp
|
||||
source Fs
|
||||
}
|
||||
|
||||
type RegexpFile struct {
|
||||
f File
|
||||
re *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewRegexpFilter(re *regexp.Regexp) FilterFs {
|
||||
return &RegexpFilter{re: re}
|
||||
}
|
||||
|
||||
// prepend a filter in the filter chain
|
||||
func (r *RegexpFilter) AddFilter(fs FilterFs) {
|
||||
fs.SetSource(r.source)
|
||||
r.source = fs
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) SetSource(fs Fs) {
|
||||
r.source = fs
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) matchesName(name string) error {
|
||||
if r.re == nil {
|
||||
return nil
|
||||
}
|
||||
if r.re.MatchString(name) {
|
||||
return nil
|
||||
}
|
||||
return syscall.ENOENT
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) dirOrMatches(name string) error {
|
||||
dir, err := IsDir(r.source, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dir {
|
||||
return nil
|
||||
}
|
||||
return r.matchesName(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Chtimes(name string, a, m time.Time) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Chtimes(name, a, m)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Chmod(name string, mode os.FileMode) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Chmod(name, mode)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Name() string {
|
||||
return "RegexpFilter"
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Stat(name string) (os.FileInfo, error) {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.Stat(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Rename(oldname, newname string) error {
|
||||
dir, err := IsDir(r.source, oldname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dir {
|
||||
return nil
|
||||
}
|
||||
if err := r.matchesName(oldname); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.matchesName(newname); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) RemoveAll(p string) error {
|
||||
dir, err := IsDir(r.source, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !dir {
|
||||
if err := r.matchesName(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return r.source.RemoveAll(p)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Remove(name string) error {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.source.Remove(name)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
if err := r.dirOrMatches(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Open(name string) (File, error) {
|
||||
dir, err := IsDir(r.source, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dir {
|
||||
if err := r.matchesName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f, err := r.source.Open(name)
|
||||
return &RegexpFile{f: f, re: r.re}, nil
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Mkdir(n string, p os.FileMode) error {
|
||||
return r.source.Mkdir(n, p)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) MkdirAll(n string, p os.FileMode) error {
|
||||
return r.source.MkdirAll(n, p)
|
||||
}
|
||||
|
||||
func (r *RegexpFilter) Create(name string) (File, error) {
|
||||
if err := r.matchesName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.source.Create(name)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Close() error {
|
||||
return f.f.Close()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Read(s []byte) (int, error) {
|
||||
return f.f.Read(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
|
||||
return f.f.ReadAt(s, o)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
|
||||
return f.f.Seek(o, w)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Write(s []byte) (int, error) {
|
||||
return f.f.Write(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
|
||||
return f.f.WriteAt(s, o)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Name() string {
|
||||
return f.f.Name()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
|
||||
var rfi []os.FileInfo
|
||||
rfi, err = f.f.Readdir(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, i := range rfi {
|
||||
if i.IsDir() || f.re.MatchString(i.Name()) {
|
||||
fi = append(fi, i)
|
||||
}
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
|
||||
fi, err := f.Readdir(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range fi {
|
||||
n = append(n, s.Name())
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Stat() (os.FileInfo, error) {
|
||||
return f.f.Stat()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Sync() error {
|
||||
return f.f.Sync()
|
||||
}
|
||||
|
||||
func (f *RegexpFile) Truncate(s int64) error {
|
||||
return f.f.Truncate(s)
|
||||
}
|
||||
|
||||
func (f *RegexpFile) WriteString(s string) (int, error) {
|
||||
return f.f.WriteString(s)
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package afero
|
||||
|
||||
import (
|
||||
// "regexp"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadOnly(t *testing.T) {
|
||||
func TestFilterReadOnly(t *testing.T) {
|
||||
mfs := &MemMapFs{}
|
||||
fs := NewFilter(mfs)
|
||||
fs.AddFilter(NewReadonlyFilter())
|
||||
|
@ -16,7 +16,7 @@ func TestReadOnly(t *testing.T) {
|
|||
t.Logf("ERR=%s", err)
|
||||
}
|
||||
|
||||
func TestReadonlyRemoveAndRead(t *testing.T) {
|
||||
func TestFilterReadonlyRemoveAndRead(t *testing.T) {
|
||||
mfs := &MemMapFs{}
|
||||
fh, err := mfs.Create("/file.txt")
|
||||
fh.Write([]byte("content here"))
|
||||
|
@ -52,11 +52,11 @@ func TestReadonlyRemoveAndRead(t *testing.T) {
|
|||
t.Errorf("File still present")
|
||||
}
|
||||
}
|
||||
/*
|
||||
func TestRegexp(t *testing.T) {
|
||||
|
||||
func TestFilterRegexp(t *testing.T) {
|
||||
mfs := &MemMapFs{}
|
||||
fs := NewFilter(mfs)
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`), nil))
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`)))
|
||||
_, err := fs.Create("/file.html")
|
||||
if err == nil {
|
||||
t.Errorf("Did not fail to create file")
|
||||
|
@ -64,15 +64,40 @@ func TestRegexp(t *testing.T) {
|
|||
t.Logf("ERR=%s", err)
|
||||
}
|
||||
|
||||
func TestRORegexpChain(t *testing.T) {
|
||||
func TestFilterRORegexpChain(t *testing.T) {
|
||||
mfs := &MemMapFs{}
|
||||
fs := NewFilter(mfs)
|
||||
fs.AddFilter(NewReadonlyFilter())
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`), nil))
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`)))
|
||||
_, err := fs.Create("/file.txt")
|
||||
if err == nil {
|
||||
t.Errorf("Did not fail to create file")
|
||||
}
|
||||
t.Logf("ERR=%s", err)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestFilterRegexReadDir(t *testing.T) {
|
||||
mfs := &MemMapFs{}
|
||||
fs := NewFilter(mfs)
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`)))
|
||||
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`^a`)))
|
||||
|
||||
mfs.MkdirAll("/dir/sub", 0777)
|
||||
for _, name := range []string{"afile.txt", "afile.html", "bfile.txt"} {
|
||||
for _, dir := range []string{"/dir/", "/dir/sub/"} {
|
||||
fh, _ := mfs.Create(dir + name)
|
||||
fh.Close()
|
||||
}
|
||||
}
|
||||
|
||||
files, _ := ReadDir(fs, "/dir")
|
||||
if len(files) != 2 { // afile.txt, sub
|
||||
t.Errorf("Got wrong number of files: %#v", files)
|
||||
}
|
||||
|
||||
f, _ := fs.Open("/dir/sub")
|
||||
names, _ := f.Readdirnames(-1)
|
||||
if len(names) != 1 {
|
||||
t.Errorf("Got wrong number of names: %v", names)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue