1package inmemory
2
3import (
4	"fmt"
5	"io"
6	"path"
7	"sort"
8	"strings"
9	"time"
10)
11
12var (
13	errExists    = fmt.Errorf("exists")
14	errNotExists = fmt.Errorf("notexists")
15	errIsNotDir  = fmt.Errorf("notdir")
16	errIsDir     = fmt.Errorf("isdir")
17)
18
19type node interface {
20	name() string
21	path() string
22	isdir() bool
23	modtime() time.Time
24}
25
26// dir is the central type for the memory-based  storagedriver. All operations
27// are dispatched from a root dir.
28type dir struct {
29	common
30
31	// TODO(stevvooe): Use sorted slice + search.
32	children map[string]node
33}
34
35var _ node = &dir{}
36
37func (d *dir) isdir() bool {
38	return true
39}
40
41// add places the node n into dir d.
42func (d *dir) add(n node) {
43	if d.children == nil {
44		d.children = make(map[string]node)
45	}
46
47	d.children[n.name()] = n
48	d.mod = time.Now()
49}
50
51// find searches for the node, given path q in dir. If the node is found, it
52// will be returned. If the node is not found, the closet existing parent. If
53// the node is found, the returned (node).path() will match q.
54func (d *dir) find(q string) node {
55	q = strings.Trim(q, "/")
56	i := strings.Index(q, "/")
57
58	if q == "" {
59		return d
60	}
61
62	if i == 0 {
63		panic("shouldn't happen, no root paths")
64	}
65
66	var component string
67	if i < 0 {
68		// No more path components
69		component = q
70	} else {
71		component = q[:i]
72	}
73
74	child, ok := d.children[component]
75	if !ok {
76		// Node was not found. Return p and the current node.
77		return d
78	}
79
80	if child.isdir() {
81		// traverse down!
82		q = q[i+1:]
83		return child.(*dir).find(q)
84	}
85
86	return child
87}
88
89func (d *dir) list(p string) ([]string, error) {
90	n := d.find(p)
91
92	if n.path() != p {
93		return nil, errNotExists
94	}
95
96	if !n.isdir() {
97		return nil, errIsNotDir
98	}
99
100	var children []string
101	for _, child := range n.(*dir).children {
102		children = append(children, child.path())
103	}
104
105	sort.Strings(children)
106	return children, nil
107}
108
109// mkfile or return the existing one. returns an error if it exists and is a
110// directory. Essentially, this is open or create.
111func (d *dir) mkfile(p string) (*file, error) {
112	n := d.find(p)
113	if n.path() == p {
114		if n.isdir() {
115			return nil, errIsDir
116		}
117
118		return n.(*file), nil
119	}
120
121	dirpath, filename := path.Split(p)
122	// Make any non-existent directories
123	n, err := d.mkdirs(dirpath)
124	if err != nil {
125		return nil, err
126	}
127
128	dd := n.(*dir)
129	n = &file{
130		common: common{
131			p:   path.Join(dd.path(), filename),
132			mod: time.Now(),
133		},
134	}
135
136	dd.add(n)
137	return n.(*file), nil
138}
139
140// mkdirs creates any missing directory entries in p and returns the result.
141func (d *dir) mkdirs(p string) (*dir, error) {
142	p = normalize(p)
143
144	n := d.find(p)
145
146	if !n.isdir() {
147		// Found something there
148		return nil, errIsNotDir
149	}
150
151	if n.path() == p {
152		return n.(*dir), nil
153	}
154
155	dd := n.(*dir)
156
157	relative := strings.Trim(strings.TrimPrefix(p, n.path()), "/")
158
159	if relative == "" {
160		return dd, nil
161	}
162
163	components := strings.Split(relative, "/")
164	for _, component := range components {
165		d, err := dd.mkdir(component)
166
167		if err != nil {
168			// This should actually never happen, since there are no children.
169			return nil, err
170		}
171		dd = d
172	}
173
174	return dd, nil
175}
176
177// mkdir creates a child directory under d with the given name.
178func (d *dir) mkdir(name string) (*dir, error) {
179	if name == "" {
180		return nil, fmt.Errorf("invalid dirname")
181	}
182
183	_, ok := d.children[name]
184	if ok {
185		return nil, errExists
186	}
187
188	child := &dir{
189		common: common{
190			p:   path.Join(d.path(), name),
191			mod: time.Now(),
192		},
193	}
194	d.add(child)
195	d.mod = time.Now()
196
197	return child, nil
198}
199
200func (d *dir) move(src, dst string) error {
201	dstDirname, _ := path.Split(dst)
202
203	dp, err := d.mkdirs(dstDirname)
204	if err != nil {
205		return err
206	}
207
208	srcDirname, srcFilename := path.Split(src)
209	sp := d.find(srcDirname)
210
211	if normalize(srcDirname) != normalize(sp.path()) {
212		return errNotExists
213	}
214
215	spd, ok := sp.(*dir)
216	if !ok {
217		return errIsNotDir // paranoid.
218	}
219
220	s, ok := spd.children[srcFilename]
221	if !ok {
222		return errNotExists
223	}
224
225	delete(spd.children, srcFilename)
226
227	switch n := s.(type) {
228	case *dir:
229		n.p = dst
230	case *file:
231		n.p = dst
232	}
233
234	dp.add(s)
235
236	return nil
237}
238
239func (d *dir) delete(p string) error {
240	dirname, filename := path.Split(p)
241	parent := d.find(dirname)
242
243	if normalize(dirname) != normalize(parent.path()) {
244		return errNotExists
245	}
246
247	if _, ok := parent.(*dir).children[filename]; !ok {
248		return errNotExists
249	}
250
251	delete(parent.(*dir).children, filename)
252	return nil
253}
254
255// dump outputs a primitive directory structure to stdout.
256func (d *dir) dump(indent string) {
257	fmt.Println(indent, d.name()+"/")
258
259	for _, child := range d.children {
260		if child.isdir() {
261			child.(*dir).dump(indent + "\t")
262		} else {
263			fmt.Println(indent, child.name())
264		}
265
266	}
267}
268
269func (d *dir) String() string {
270	return fmt.Sprintf("&dir{path: %v, children: %v}", d.p, d.children)
271}
272
273// file stores actual data in the fs tree. It acts like an open, seekable file
274// where operations are conducted through ReadAt and WriteAt. Use it with
275// SectionReader for the best effect.
276type file struct {
277	common
278	data []byte
279}
280
281var _ node = &file{}
282
283func (f *file) isdir() bool {
284	return false
285}
286
287func (f *file) truncate() {
288	f.data = f.data[:0]
289}
290
291func (f *file) sectionReader(offset int64) io.Reader {
292	return io.NewSectionReader(f, offset, int64(len(f.data))-offset)
293}
294
295func (f *file) ReadAt(p []byte, offset int64) (n int, err error) {
296	return copy(p, f.data[offset:]), nil
297}
298
299func (f *file) WriteAt(p []byte, offset int64) (n int, err error) {
300	off := int(offset)
301	if cap(f.data) < off+len(p) {
302		data := make([]byte, len(f.data), off+len(p))
303		copy(data, f.data)
304		f.data = data
305	}
306
307	f.mod = time.Now()
308	f.data = f.data[:off+len(p)]
309
310	return copy(f.data[off:off+len(p)], p), nil
311}
312
313func (f *file) String() string {
314	return fmt.Sprintf("&file{path: %q}", f.p)
315}
316
317// common provides shared fields and methods for node implementations.
318type common struct {
319	p   string
320	mod time.Time
321}
322
323func (c *common) name() string {
324	_, name := path.Split(c.p)
325	return name
326}
327
328func (c *common) path() string {
329	return c.p
330}
331
332func (c *common) modtime() time.Time {
333	return c.mod
334}
335
336func normalize(p string) string {
337	return "/" + strings.Trim(p, "/")
338}
339