commands: Moved files code into 'commands/files' subpackage
Matt Bell committed
Jan 13, 2015 at 10:16 UTC
7a579bbf79c8cedfb063e35197982d85a2cf30d1
11 files changed
+215
-201
commands/cli/parse.go
+12
-11
@@ -11,6 +11,7 @@ import (
11
"strings"
12
13
cmds "github.com/jbenet/go-ipfs/commands"
14
+ cmdsFiles "github.com/jbenet/go-ipfs/commands/files"
15
u "github.com/jbenet/go-ipfs/util"
16
)
17
@@ -65,7 +66,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
66
}
67
req.SetArguments(stringArgs)
68
68
- file := &cmds.SliceFile{"", fileArgs}
69
+ file := &cmdsFiles.SliceFile{"", fileArgs}
70
req.SetFiles(file)
71
72
err = cmd.CheckArguments(req)
@@ -139,7 +140,7 @@ func parseOptions(input []string) (map[string]interface{}, []string, error) {
140
return opts, args, nil
141
}
142
142
-func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []cmds.File, error) {
143
+func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []cmdsFiles.File, error) {
144
// ignore stdin on Windows
145
if runtime.GOOS == "windows" {
146
stdin = nil
@@ -176,7 +177,7 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
177
}
178
179
stringArgs := make([]string, 0, numInputs)
179
- fileArgs := make([]cmds.File, 0, numInputs)
180
+ fileArgs := make([]cmdsFiles.File, 0, numInputs)
181
182
argDefIndex := 0 // the index of the current argument definition
183
for i := 0; i < numInputs; i++ {
@@ -263,7 +264,7 @@ func appendStdinAsString(args []string, stdin *os.File) ([]string, *os.File, err
264
return append(args, buf.String()), nil, nil
265
}
266
266
-func appendFile(args []cmds.File, inputs []string, argDef *cmds.Argument, recursive bool) ([]cmds.File, []string, error) {
267
+func appendFile(args []cmdsFiles.File, inputs []string, argDef *cmds.Argument, recursive bool) ([]cmdsFiles.File, []string, error) {
268
path := inputs[0]
269
270
file, err := os.Open(path)
@@ -297,13 +298,13 @@ func appendFile(args []cmds.File, inputs []string, argDef *cmds.Argument, recurs
298
return append(args, arg), inputs[1:], nil
299
}
300
300
-func appendStdinAsFile(args []cmds.File, stdin *os.File) ([]cmds.File, *os.File) {
301
- arg := &cmds.ReaderFile{"", stdin}
301
+func appendStdinAsFile(args []cmdsFiles.File, stdin *os.File) ([]cmdsFiles.File, *os.File) {
302
+ arg := &cmdsFiles.ReaderFile{"", stdin}
303
return append(args, arg), nil
304
}
305
305
-// recursively get file or directory contents as a cmds.File
306
-func openPath(file *os.File, path string) (cmds.File, error) {
306
+// recursively get file or directory contents as a cmdsFiles.File
307
+func openPath(file *os.File, path string) (cmdsFiles.File, error) {
308
stat, err := file.Stat()
309
if err != nil {
310
return nil, err
@@ -311,7 +312,7 @@ func openPath(file *os.File, path string) (cmds.File, error) {
312
313
// for non-directories, return a ReaderFile
314
if !stat.IsDir() {
314
- return &cmds.ReaderFile{path, file}, nil
315
+ return &cmdsFiles.ReaderFile{path, file}, nil
316
}
317
318
// for directories, recursively iterate though children then return as a SliceFile
@@ -323,7 +324,7 @@ func openPath(file *os.File, path string) (cmds.File, error) {
324
// make sure contents are sorted so -- repeatably -- we get the same inputs.
325
sort.Sort(sortFIByName(contents))
326
326
- files := make([]cmds.File, 0, len(contents))
327
+ files := make([]cmdsFiles.File, 0, len(contents))
328
for _, child := range contents {
329
childPath := fp.Join(path, child.Name())
330
childFile, err := os.Open(childPath)
@@ -339,7 +340,7 @@ func openPath(file *os.File, path string) (cmds.File, error) {
340
files = append(files, f)
341
}
342
342
- return &cmds.SliceFile{path, files}, nil
343
+ return &cmdsFiles.SliceFile{path, files}, nil
344
}
345
346
// isTerminal returns true if stdin is a Stdin pipe (e.g. `cat file | ipfs`),
commands/file.go
deleted
-176
@@ -1,176 +0,0 @@
1
-package commands
2
-
3
-import (
4
- "errors"
5
- "io"
6
- "mime"
7
- "mime/multipart"
8
- "net/http"
9
-)
10
-
11
-const (
12
- multipartFormdataType = "multipart/form-data"
13
- multipartMixedType = "multipart/mixed"
14
-
15
- contentTypeHeader = "Content-Type"
16
-)
17
-
18
-var (
19
- ErrNotDirectory = errors.New("Couln't call NextFile(), this isn't a directory")
20
- ErrNotReader = errors.New("This file is a directory, can't use Reader functions")
21
-)
22
-
23
-// File is an interface that provides functionality for handling files/directories
24
-// as values that can be supplied to commands. For directories, child files are
25
-// accessed serially by calling `NextFile()`.
26
-type File interface {
27
- // Files implement ReadCloser, but can only be read from or closed if they are not directories
28
- io.ReadCloser
29
-
30
- // FileName returns a full filename path associated with this file
31
- FileName() string
32
-
33
- // IsDirectory returns true if the File is a directory (and therefore supports calling `NextFile`)
34
- // and false if the File is a normal file (and therefor supports calling `Read` and `Close`)
35
- IsDirectory() bool
36
-
37
- // NextFile returns the next child file available (if the File is a directory).
38
- // It will return (nil, io.EOF) if no more files are available.
39
- // If the file is a regular file (not a directory), NextFile will return a non-nil error.
40
- NextFile() (File, error)
41
-}
42
-
43
-// MultipartFile implements File, and is created from a `multipart.Part`.
44
-// It can be either a directory or file (checked by calling `IsDirectory()`).
45
-type MultipartFile struct {
46
- File
47
-
48
- Part *multipart.Part
49
- Reader *multipart.Reader
50
- Mediatype string
51
-}
52
-
53
-func NewFileFromPart(part *multipart.Part) (File, error) {
54
- f := &MultipartFile{
55
- Part: part,
56
- }
57
-
58
- contentType := part.Header.Get(contentTypeHeader)
59
-
60
- var params map[string]string
61
- var err error
62
- f.Mediatype, params, err = mime.ParseMediaType(contentType)
63
- if err != nil {
64
- return nil, err
65
- }
66
-
67
- if f.IsDirectory() {
68
- boundary, found := params["boundary"]
69
- if !found {
70
- return nil, http.ErrMissingBoundary
71
- }
72
-
73
- f.Reader = multipart.NewReader(part, boundary)
74
- }
75
-
76
- return f, nil
77
-}
78
-
79
-func (f *MultipartFile) IsDirectory() bool {
80
- return f.Mediatype == multipartFormdataType || f.Mediatype == multipartMixedType
81
-}
82
-
83
-func (f *MultipartFile) NextFile() (File, error) {
84
- if !f.IsDirectory() {
85
- return nil, ErrNotDirectory
86
- }
87
-
88
- part, err := f.Reader.NextPart()
89
- if err != nil {
90
- return nil, err
91
- }
92
-
93
- return NewFileFromPart(part)
94
-}
95
-
96
-func (f *MultipartFile) FileName() string {
97
- return f.Part.FileName()
98
-}
99
-
100
-func (f *MultipartFile) Read(p []byte) (int, error) {
101
- if f.IsDirectory() {
102
- return 0, ErrNotReader
103
- }
104
- return f.Part.Read(p)
105
-}
106
-
107
-func (f *MultipartFile) Close() error {
108
- if f.IsDirectory() {
109
- return ErrNotReader
110
- }
111
- return f.Part.Close()
112
-}
113
-
114
-// SliceFile implements File, and provides simple directory handling.
115
-// It contains children files, and is created from a `[]File`.
116
-// SliceFiles are always directories, and can't be read from or closed.
117
-type SliceFile struct {
118
- Filename string
119
- Files []File
120
-}
121
-
122
-func (f *SliceFile) IsDirectory() bool {
123
- return true
124
-}
125
-
126
-func (f *SliceFile) NextFile() (File, error) {
127
- if len(f.Files) == 0 {
128
- return nil, io.EOF
129
- }
130
- file := f.Files[0]
131
- f.Files = f.Files[1:]
132
- return file, nil
133
-}
134
-
135
-func (f *SliceFile) FileName() string {
136
- return f.Filename
137
-}
138
-
139
-func (f *SliceFile) Read(p []byte) (int, error) {
140
- return 0, ErrNotReader
141
-}
142
-
143
-func (f *SliceFile) Close() error {
144
- return ErrNotReader
145
-}
146
-
147
-// ReaderFile is a implementation of File created from an `io.Reader`.
148
-// ReaderFiles are never directories, and can be read from and closed.
149
-type ReaderFile struct {
150
- Filename string
151
- Reader io.Reader
152
-}
153
-
154
-func (f *ReaderFile) IsDirectory() bool {
155
- return false
156
-}
157
-
158
-func (f *ReaderFile) NextFile() (File, error) {
159
- return nil, ErrNotDirectory
160
-}
161
-
162
-func (f *ReaderFile) FileName() string {
163
- return f.Filename
164
-}
165
-
166
-func (f *ReaderFile) Read(p []byte) (int, error) {
167
- return f.Reader.Read(p)
168
-}
169
-
170
-func (f *ReaderFile) Close() error {
171
- closer, ok := f.Reader.(io.Closer)
172
- if !ok {
173
- return nil
174
- }
175
- return closer.Close()
176
-}
commands/files/file.go
new
+31
@@ -0,0 +1,31 @@
1
+package files
2
+
3
+import (
4
+ "errors"
5
+ "io"
6
+)
7
+
8
+var (
9
+ ErrNotDirectory = errors.New("Couln't call NextFile(), this isn't a directory")
10
+ ErrNotReader = errors.New("This file is a directory, can't use Reader functions")
11
+)
12
+
13
+// File is an interface that provides functionality for handling files/directories
14
+// as values that can be supplied to commands. For directories, child files are
15
+// accessed serially by calling `NextFile()`.
16
+type File interface {
17
+ // Files implement ReadCloser, but can only be read from or closed if they are not directories
18
+ io.ReadCloser
19
+
20
+ // FileName returns a full filename path associated with this file
21
+ FileName() string
22
+
23
+ // IsDirectory returns true if the File is a directory (and therefore supports calling `NextFile`)
24
+ // and false if the File is a normal file (and therefor supports calling `Read` and `Close`)
25
+ IsDirectory() bool
26
+
27
+ // NextFile returns the next child file available (if the File is a directory).
28
+ // It will return (nil, io.EOF) if no more files are available.
29
+ // If the file is a regular file (not a directory), NextFile will return a non-nil error.
30
+ NextFile() (File, error)
31
+}
commands/files/file_test.go
renamed
+1
-1
@@ -1,4 +1,4 @@
1
-package commands
1
+package files
2
3
import (
4
"io"
commands/files/multipartfile.go
new
+85
@@ -0,0 +1,85 @@
1
+package files
2
+
3
+import (
4
+ "mime"
5
+ "mime/multipart"
6
+ "net/http"
7
+)
8
+
9
+const (
10
+ multipartFormdataType = "multipart/form-data"
11
+ multipartMixedType = "multipart/mixed"
12
+
13
+ contentTypeHeader = "Content-Type"
14
+)
15
+
16
+// MultipartFile implements File, and is created from a `multipart.Part`.
17
+// It can be either a directory or file (checked by calling `IsDirectory()`).
18
+type MultipartFile struct {
19
+ File
20
+
21
+ Part *multipart.Part
22
+ Reader *multipart.Reader
23
+ Mediatype string
24
+}
25
+
26
+func NewFileFromPart(part *multipart.Part) (File, error) {
27
+ f := &MultipartFile{
28
+ Part: part,
29
+ }
30
+
31
+ contentType := part.Header.Get(contentTypeHeader)
32
+
33
+ var params map[string]string
34
+ var err error
35
+ f.Mediatype, params, err = mime.ParseMediaType(contentType)
36
+ if err != nil {
37
+ return nil, err
38
+ }
39
+
40
+ if f.IsDirectory() {
41
+ boundary, found := params["boundary"]
42
+ if !found {
43
+ return nil, http.ErrMissingBoundary
44
+ }
45
+
46
+ f.Reader = multipart.NewReader(part, boundary)
47
+ }
48
+
49
+ return f, nil
50
+}
51
+
52
+func (f *MultipartFile) IsDirectory() bool {
53
+ return f.Mediatype == multipartFormdataType || f.Mediatype == multipartMixedType
54
+}
55
+
56
+func (f *MultipartFile) NextFile() (File, error) {
57
+ if !f.IsDirectory() {
58
+ return nil, ErrNotDirectory
59
+ }
60
+
61
+ part, err := f.Reader.NextPart()
62
+ if err != nil {
63
+ return nil, err
64
+ }
65
+
66
+ return NewFileFromPart(part)
67
+}
68
+
69
+func (f *MultipartFile) FileName() string {
70
+ return f.Part.FileName()
71
+}
72
+
73
+func (f *MultipartFile) Read(p []byte) (int, error) {
74
+ if f.IsDirectory() {
75
+ return 0, ErrNotReader
76
+ }
77
+ return f.Part.Read(p)
78
+}
79
+
80
+func (f *MultipartFile) Close() error {
81
+ if f.IsDirectory() {
82
+ return ErrNotReader
83
+ }
84
+ return f.Part.Close()
85
+}
commands/files/readerfile.go
new
+34
@@ -0,0 +1,34 @@
1
+package files
2
+
3
+import "io"
4
+
5
+// ReaderFile is a implementation of File created from an `io.Reader`.
6
+// ReaderFiles are never directories, and can be read from and closed.
7
+type ReaderFile struct {
8
+ Filename string
9
+ Reader io.Reader
10
+}
11
+
12
+func (f *ReaderFile) IsDirectory() bool {
13
+ return false
14
+}
15
+
16
+func (f *ReaderFile) NextFile() (File, error) {
17
+ return nil, ErrNotDirectory
18
+}
19
+
20
+func (f *ReaderFile) FileName() string {
21
+ return f.Filename
22
+}
23
+
24
+func (f *ReaderFile) Read(p []byte) (int, error) {
25
+ return f.Reader.Read(p)
26
+}
27
+
28
+func (f *ReaderFile) Close() error {
29
+ closer, ok := f.Reader.(io.Closer)
30
+ if !ok {
31
+ return nil
32
+ }
33
+ return closer.Close()
34
+}
commands/files/slicefile.go
new
+36
@@ -0,0 +1,36 @@
1
+package files
2
+
3
+import "io"
4
+
5
+// SliceFile implements File, and provides simple directory handling.
6
+// It contains children files, and is created from a `[]File`.
7
+// SliceFiles are always directories, and can't be read from or closed.
8
+type SliceFile struct {
9
+ Filename string
10
+ Files []File
11
+}
12
+
13
+func (f *SliceFile) IsDirectory() bool {
14
+ return true
15
+}
16
+
17
+func (f *SliceFile) NextFile() (File, error) {
18
+ if len(f.Files) == 0 {
19
+ return nil, io.EOF
20
+ }
21
+ file := f.Files[0]
22
+ f.Files = f.Files[1:]
23
+ return file, nil
24
+}
25
+
26
+func (f *SliceFile) FileName() string {
27
+ return f.Filename
28
+}
29
+
30
+func (f *SliceFile) Read(p []byte) (int, error) {
31
+ return 0, ErrNotReader
32
+}
33
+
34
+func (f *SliceFile) Close() error {
35
+ return ErrNotReader
36
+}
commands/http/multifilereader.go
+3
-3
@@ -8,7 +8,7 @@ import (
8
"net/textproto"
9
"sync"
10
11
- cmds "github.com/jbenet/go-ipfs/commands"
11
+ files "github.com/jbenet/go-ipfs/commands/files"
12
)
13
14
// MultiFileReader reads from a `commands.File` (which can be a directory of files
@@ -16,7 +16,7 @@ import (
16
type MultiFileReader struct {
17
io.Reader
18
19
- files cmds.File
19
+ files files.File
20
currentFile io.Reader
21
buf bytes.Buffer
22
mpWriter *multipart.Writer
@@ -31,7 +31,7 @@ type MultiFileReader struct {
31
// NewMultiFileReader constructs a MultiFileReader. `file` can be any `commands.File`.
32
// If `form` is set to true, the multipart data will have a Content-Type of 'multipart/form-data',
33
// if `form` is false, the Content-Type will be 'multipart/mixed'.
34
-func NewMultiFileReader(file cmds.File, form bool) *MultiFileReader {
34
+func NewMultiFileReader(file files.File, form bool) *MultiFileReader {
35
mfr := &MultiFileReader{
36
files: file,
37
form: form,
commands/http/parse.go
+3
-2
@@ -8,6 +8,7 @@ import (
8
"strings"
9
10
cmds "github.com/jbenet/go-ipfs/commands"
11
+ files "github.com/jbenet/go-ipfs/commands/files"
12
)
13
14
// Parse parses the data in a http.Request and returns a command Request object
@@ -94,9 +95,9 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) {
95
contentType := r.Header.Get(contentTypeHeader)
96
mediatype, _, _ := mime.ParseMediaType(contentType)
97
97
- var f *cmds.MultipartFile
98
+ var f *files.MultipartFile
99
if mediatype == "multipart/form-data" {
99
- f = &cmds.MultipartFile{Mediatype: mediatype}
100
+ f = &files.MultipartFile{Mediatype: mediatype}
101
f.Reader, err = r.MultipartReader()
102
if err != nil {
103
return nil, err
commands/request.go
+7
-6
@@ -8,6 +8,7 @@ import (
8
9
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
10
11
+ "github.com/jbenet/go-ipfs/commands/files"
12
"github.com/jbenet/go-ipfs/core"
13
"github.com/jbenet/go-ipfs/repo/config"
14
u "github.com/jbenet/go-ipfs/util"
@@ -71,8 +72,8 @@ type Request interface {
72
SetOptions(opts map[string]interface{}) error
73
Arguments() []string
74
SetArguments([]string)
74
- Files() File
75
- SetFiles(File)
75
+ Files() files.File
76
+ SetFiles(files.File)
77
Context() *Context
78
SetContext(Context)
79
Command() *Command
@@ -84,7 +85,7 @@ type request struct {
85
path []string
86
options optMap
87
arguments []string
87
- files File
88
+ files files.File
89
cmd *Command
90
ctx Context
91
optionDefs map[string]Option
@@ -159,11 +160,11 @@ func (r *request) SetArguments(args []string) {
160
r.arguments = args
161
}
162
162
-func (r *request) Files() File {
163
+func (r *request) Files() files.File {
164
return r.files
165
}
166
166
-func (r *request) SetFiles(f File) {
167
+func (r *request) SetFiles(f files.File) {
168
r.files = f
169
}
170
@@ -259,7 +260,7 @@ func NewEmptyRequest() (Request, error) {
260
261
// NewRequest returns a request initialized with given arguments
262
// An non-nil error will be returned if the provided option values are invalid
262
-func NewRequest(path []string, opts optMap, args []string, file File, cmd *Command, optDefs map[string]Option) (Request, error) {
263
+func NewRequest(path []string, opts optMap, args []string, file files.File, cmd *Command, optDefs map[string]Option) (Request, error) {
264
if path == nil {
265
path = make([]string, 0)
266
}
core/commands/add.go
+3
-2
@@ -8,6 +8,7 @@ import (
8
"path"
9
10
cmds "github.com/jbenet/go-ipfs/commands"
11
+ files "github.com/jbenet/go-ipfs/commands/files"
12
core "github.com/jbenet/go-ipfs/core"
13
importer "github.com/jbenet/go-ipfs/importer"
14
"github.com/jbenet/go-ipfs/importer/chunk"
@@ -138,7 +139,7 @@ func addNode(n *core.IpfsNode, node *dag.Node) error {
139
return nil
140
}
141
141
-func addFile(n *core.IpfsNode, file cmds.File, out chan interface{}) (*dag.Node, error) {
142
+func addFile(n *core.IpfsNode, file files.File, out chan interface{}) (*dag.Node, error) {
143
if file.IsDirectory() {
144
return addDir(n, file, out)
145
}
@@ -155,7 +156,7 @@ func addFile(n *core.IpfsNode, file cmds.File, out chan interface{}) (*dag.Node,
156
return dns[len(dns)-1], nil // last dag node is the file.
157
}
158
158
-func addDir(n *core.IpfsNode, dir cmds.File, out chan interface{}) (*dag.Node, error) {
159
+func addDir(n *core.IpfsNode, dir files.File, out chan interface{}) (*dag.Node, error) {
160
log.Infof("adding directory: %s", dir.FileName())
161
162
tree := &dag.Node{Data: ft.FolderPBData()}