1package afero
2
3import (
4	"os"
5	"regexp"
6	"syscall"
7	"time"
8)
9
10// The RegexpFs filters files (not directories) by regular expression. Only
11// files matching the given regexp will be allowed, all others get a ENOENT error (
12// "No such file or directory").
13//
14type RegexpFs struct {
15	re     *regexp.Regexp
16	source Fs
17}
18
19func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
20	return &RegexpFs{source: source, re: re}
21}
22
23type RegexpFile struct {
24	f  File
25	re *regexp.Regexp
26}
27
28func (r *RegexpFs) matchesName(name string) error {
29	if r.re == nil {
30		return nil
31	}
32	if r.re.MatchString(name) {
33		return nil
34	}
35	return syscall.ENOENT
36}
37
38func (r *RegexpFs) dirOrMatches(name string) error {
39	dir, err := IsDir(r.source, name)
40	if err != nil {
41		return err
42	}
43	if dir {
44		return nil
45	}
46	return r.matchesName(name)
47}
48
49func (r *RegexpFs) Chtimes(name string, a, m time.Time) error {
50	if err := r.dirOrMatches(name); err != nil {
51		return err
52	}
53	return r.source.Chtimes(name, a, m)
54}
55
56func (r *RegexpFs) Chmod(name string, mode os.FileMode) error {
57	if err := r.dirOrMatches(name); err != nil {
58		return err
59	}
60	return r.source.Chmod(name, mode)
61}
62
63func (r *RegexpFs) Name() string {
64	return "RegexpFs"
65}
66
67func (r *RegexpFs) Stat(name string) (os.FileInfo, error) {
68	if err := r.dirOrMatches(name); err != nil {
69		return nil, err
70	}
71	return r.source.Stat(name)
72}
73
74func (r *RegexpFs) Rename(oldname, newname string) error {
75	dir, err := IsDir(r.source, oldname)
76	if err != nil {
77		return err
78	}
79	if dir {
80		return nil
81	}
82	if err := r.matchesName(oldname); err != nil {
83		return err
84	}
85	if err := r.matchesName(newname); err != nil {
86		return err
87	}
88	return r.source.Rename(oldname, newname)
89}
90
91func (r *RegexpFs) RemoveAll(p string) error {
92	dir, err := IsDir(r.source, p)
93	if err != nil {
94		return err
95	}
96	if !dir {
97		if err := r.matchesName(p); err != nil {
98			return err
99		}
100	}
101	return r.source.RemoveAll(p)
102}
103
104func (r *RegexpFs) Remove(name string) error {
105	if err := r.dirOrMatches(name); err != nil {
106		return err
107	}
108	return r.source.Remove(name)
109}
110
111func (r *RegexpFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
112	if err := r.dirOrMatches(name); err != nil {
113		return nil, err
114	}
115	return r.source.OpenFile(name, flag, perm)
116}
117
118func (r *RegexpFs) Open(name string) (File, error) {
119	dir, err := IsDir(r.source, name)
120	if err != nil {
121		return nil, err
122	}
123	if !dir {
124		if err := r.matchesName(name); err != nil {
125			return nil, err
126		}
127	}
128	f, err := r.source.Open(name)
129	if err != nil {
130		return nil, err
131	}
132	return &RegexpFile{f: f, re: r.re}, nil
133}
134
135func (r *RegexpFs) Mkdir(n string, p os.FileMode) error {
136	return r.source.Mkdir(n, p)
137}
138
139func (r *RegexpFs) MkdirAll(n string, p os.FileMode) error {
140	return r.source.MkdirAll(n, p)
141}
142
143func (r *RegexpFs) Create(name string) (File, error) {
144	if err := r.matchesName(name); err != nil {
145		return nil, err
146	}
147	return r.source.Create(name)
148}
149
150func (f *RegexpFile) Close() error {
151	return f.f.Close()
152}
153
154func (f *RegexpFile) Read(s []byte) (int, error) {
155	return f.f.Read(s)
156}
157
158func (f *RegexpFile) ReadAt(s []byte, o int64) (int, error) {
159	return f.f.ReadAt(s, o)
160}
161
162func (f *RegexpFile) Seek(o int64, w int) (int64, error) {
163	return f.f.Seek(o, w)
164}
165
166func (f *RegexpFile) Write(s []byte) (int, error) {
167	return f.f.Write(s)
168}
169
170func (f *RegexpFile) WriteAt(s []byte, o int64) (int, error) {
171	return f.f.WriteAt(s, o)
172}
173
174func (f *RegexpFile) Name() string {
175	return f.f.Name()
176}
177
178func (f *RegexpFile) Readdir(c int) (fi []os.FileInfo, err error) {
179	var rfi []os.FileInfo
180	rfi, err = f.f.Readdir(c)
181	if err != nil {
182		return nil, err
183	}
184	for _, i := range rfi {
185		if i.IsDir() || f.re.MatchString(i.Name()) {
186			fi = append(fi, i)
187		}
188	}
189	return fi, nil
190}
191
192func (f *RegexpFile) Readdirnames(c int) (n []string, err error) {
193	fi, err := f.Readdir(c)
194	if err != nil {
195		return nil, err
196	}
197	for _, s := range fi {
198		n = append(n, s.Name())
199	}
200	return n, nil
201}
202
203func (f *RegexpFile) Stat() (os.FileInfo, error) {
204	return f.f.Stat()
205}
206
207func (f *RegexpFile) Sync() error {
208	return f.f.Sync()
209}
210
211func (f *RegexpFile) Truncate(s int64) error {
212	return f.f.Truncate(s)
213}
214
215func (f *RegexpFile) WriteString(s string) (int, error) {
216	return f.f.WriteString(s)
217}
218