main
Raw Download raw file
  1package memfs
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"io"
  7	"os"
  8	"path/filepath"
  9	"strings"
 10	"sync"
 11)
 12
 13type storage struct {
 14	files    map[string]*file
 15	children map[string]map[string]*file
 16}
 17
 18func newStorage() *storage {
 19	return &storage{
 20		files:    make(map[string]*file, 0),
 21		children: make(map[string]map[string]*file, 0),
 22	}
 23}
 24
 25func (s *storage) Has(path string) bool {
 26	path = clean(path)
 27
 28	_, ok := s.files[path]
 29	return ok
 30}
 31
 32func (s *storage) New(path string, mode os.FileMode, flag int) (*file, error) {
 33	path = clean(path)
 34	if s.Has(path) {
 35		if !s.MustGet(path).mode.IsDir() {
 36			return nil, fmt.Errorf("file already exists %q", path)
 37		}
 38
 39		return nil, nil
 40	}
 41
 42	name := filepath.Base(path)
 43
 44	f := &file{
 45		name:    name,
 46		content: &content{name: name},
 47		mode:    mode,
 48		flag:    flag,
 49	}
 50
 51	s.files[path] = f
 52	s.createParent(path, mode, f)
 53	return f, nil
 54}
 55
 56func (s *storage) createParent(path string, mode os.FileMode, f *file) error {
 57	base := filepath.Dir(path)
 58	base = clean(base)
 59	if f.Name() == string(separator) {
 60		return nil
 61	}
 62
 63	if _, err := s.New(base, mode.Perm()|os.ModeDir, 0); err != nil {
 64		return err
 65	}
 66
 67	if _, ok := s.children[base]; !ok {
 68		s.children[base] = make(map[string]*file, 0)
 69	}
 70
 71	s.children[base][f.Name()] = f
 72	return nil
 73}
 74
 75func (s *storage) Children(path string) []*file {
 76	path = clean(path)
 77
 78	l := make([]*file, 0)
 79	for _, f := range s.children[path] {
 80		l = append(l, f)
 81	}
 82
 83	return l
 84}
 85
 86func (s *storage) MustGet(path string) *file {
 87	f, ok := s.Get(path)
 88	if !ok {
 89		panic(fmt.Errorf("couldn't find %q", path))
 90	}
 91
 92	return f
 93}
 94
 95func (s *storage) Get(path string) (*file, bool) {
 96	path = clean(path)
 97	if !s.Has(path) {
 98		return nil, false
 99	}
100
101	file, ok := s.files[path]
102	return file, ok
103}
104
105func (s *storage) Rename(from, to string) error {
106	from = clean(from)
107	to = clean(to)
108
109	if !s.Has(from) {
110		return os.ErrNotExist
111	}
112
113	move := [][2]string{{from, to}}
114
115	for pathFrom := range s.files {
116		if pathFrom == from || !strings.HasPrefix(pathFrom, from) {
117			continue
118		}
119
120		rel, _ := filepath.Rel(from, pathFrom)
121		pathTo := filepath.Join(to, rel)
122
123		move = append(move, [2]string{pathFrom, pathTo})
124	}
125
126	for _, ops := range move {
127		from := ops[0]
128		to := ops[1]
129
130		if err := s.move(from, to); err != nil {
131			return err
132		}
133	}
134
135	return nil
136}
137
138func (s *storage) move(from, to string) error {
139	s.files[to] = s.files[from]
140	s.files[to].name = filepath.Base(to)
141	s.children[to] = s.children[from]
142
143	defer func() {
144		delete(s.children, from)
145		delete(s.files, from)
146		delete(s.children[filepath.Dir(from)], filepath.Base(from))
147	}()
148
149	return s.createParent(to, 0644, s.files[to])
150}
151
152func (s *storage) Remove(path string) error {
153	path = clean(path)
154
155	f, has := s.Get(path)
156	if !has {
157		return os.ErrNotExist
158	}
159
160	if f.mode.IsDir() && len(s.children[path]) != 0 {
161		return fmt.Errorf("dir: %s contains files", path)
162	}
163
164	base, file := filepath.Split(path)
165	base = filepath.Clean(base)
166
167	delete(s.children[base], file)
168	delete(s.files, path)
169	return nil
170}
171
172func clean(path string) string {
173	return filepath.Clean(filepath.FromSlash(path))
174}
175
176type content struct {
177	name  string
178	bytes []byte
179
180	m sync.RWMutex
181}
182
183func (c *content) WriteAt(p []byte, off int64) (int, error) {
184	if off < 0 {
185		return 0, &os.PathError{
186			Op:   "writeat",
187			Path: c.name,
188			Err:  errors.New("negative offset"),
189		}
190	}
191
192	c.m.Lock()
193	prev := len(c.bytes)
194
195	diff := int(off) - prev
196	if diff > 0 {
197		c.bytes = append(c.bytes, make([]byte, diff)...)
198	}
199
200	c.bytes = append(c.bytes[:off], p...)
201	if len(c.bytes) < prev {
202		c.bytes = c.bytes[:prev]
203	}
204	c.m.Unlock()
205
206	return len(p), nil
207}
208
209func (c *content) ReadAt(b []byte, off int64) (n int, err error) {
210	if off < 0 {
211		return 0, &os.PathError{
212			Op:   "readat",
213			Path: c.name,
214			Err:  errors.New("negative offset"),
215		}
216	}
217
218	c.m.RLock()
219	size := int64(len(c.bytes))
220	if off >= size {
221		c.m.RUnlock()
222		return 0, io.EOF
223	}
224
225	l := int64(len(b))
226	if off+l > size {
227		l = size - off
228	}
229
230	btr := c.bytes[off : off+l]
231	n = copy(b, btr)
232
233	if len(btr) < len(b) {
234		err = io.EOF
235	}
236	c.m.RUnlock()
237
238	return
239}