~rbn/neinp

ref: b441da6dea2e963387a86b9d1d57802b77f21b83 neinp/fs/entry.go -rw-r--r-- 3.5 KiB
b441da6dRuben Schuller initial commit 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*Package fs provides helpers to implement 9p objects: directories and files.*/
package fs

import (
	"github.com/rbns/neinp/message"
	"github.com/rbns/neinp/qid"
	"github.com/rbns/neinp/stat"
	"errors"
	"io"
)

//Entry is the general interface for 9p objects.
type Entry interface {
	Parent() Entry              // Parent returns this objects parent, it may be nil for the root object.
	Qid() qid.Qid               // Qid returns this files qid.
	Stat() stat.Stat            // Stat returns this files stat.
	Open() error                // Open prepares this file for access.
	Walk(string) (Entry, error) // Walk to a child or itself.
	io.ReadSeeker               // All files and directories need this.
}

//Dir implements a directory.
type Dir struct {
	parent   Entry
	stat     stat.Stat
	children []Entry
	*stat.Reader
}

//NewDir creates a new Dir ready for use.
//
//The stat and child entries are expected to be prepared by the caller.
//Children will have their parent set to this Dir.
func NewDir(s stat.Stat, children []Entry) *Dir {
	d := &Dir{
		stat: s,
	}

	for _, child := range children {
		switch t := child.(type) {
		case *Dir:
			t.parent = d
		case *File:
			t.parent = d
		}
	}

	d.children = children

	// just initialize with an empty stat.Reader here so we dont' hit nil
	d.Reader = stat.NewReader()

	return d
}

//Parent returns the parent of this dir, which may be nil if it is the root.
func (d *Dir) Parent() Entry {
	return d.parent
}

//Qid returns the qid of this dir.
func (d *Dir) Qid() qid.Qid {
	return d.stat.Qid
}

//Stat returns the stat of this dir.
func (d *Dir) Stat() stat.Stat {
	return d.stat
}

//Open prepares the dir for access.
//
//Internally, a stat.Reader is prepared from the contents of the children.
func (d *Dir) Open() error {
	stats := make([]stat.Stat, len(d.children))
	for i, child := range d.children {
		stats[i] = child.Stat()
	}

	d.Reader = stat.NewReader(stats...)
	return nil
}

//Walk to a child, the parent, or itself.
//
//wname is either the name of a child, "..", or "".
//If it is the name of a child, the respective entry is returned.
//For ".." the parent, or the dir itself (if it is the root) is returned.
//When wname is the empty string, the dir itself is returned.
func (d *Dir) Walk(wname string) (Entry, error) {
	if wname == ".." {
		if d.parent == nil {
			return d, nil
		}
		return d.parent, nil
	}

	for _, child := range d.children {
		if child.Stat().Name == wname {
			return child, nil
		}
	}

	return nil, errors.New(message.NotFoundErrorString)
}

//File implements a file.
type File struct {
	parent Entry
	stat   stat.Stat

	// this is the minimal interface we need, as a 9p read can also seek
	io.ReadSeeker
}

//NewFile prepares a new File ready for use.
//
//The stat and ReadSeeker are expected to be prepared by the caller.
func NewFile(s stat.Stat, rs io.ReadSeeker) *File {
	f := &File{
		stat:       s,
		ReadSeeker: rs,
	}

	return f
}

//Parent returns the parent.
func (f *File) Parent() Entry {
	return f.parent
}

//Qid returns the qid.
func (f *File) Qid() qid.Qid {
	return f.stat.Qid
}

//Stat returns the stat.
func (f *File) Stat() stat.Stat {
	return f.stat
}

//Walk can only walk to itself for plain files.
//
//It is used for the side effect of creating another fid for this file.
func (f *File) Walk(wname string) (Entry, error) {
	if len(wname) != 0 {
		return nil, errors.New(message.WalkNoDirErrorString)
	}

	return f, nil
}

//Open the file for access.
//
//This is a nop here and to be overriden by embedders.
func (f *File) Open() error {
	return nil
}