Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-dms3-cmds
Commits
42c9a8a3
Commit
42c9a8a3
authored
Jan 23, 2015
by
Juan Batiz-Benet
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #613 from jbenet/progress-bars
Progress Bars
parents
ac37a321
8ae2b2aa
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
218 additions
and
59 deletions
+218
-59
cli/parse.go
cli/parse.go
+2
-2
command.go
command.go
+6
-20
command_test.go
command_test.go
+2
-2
files/file.go
files/file.go
+20
-0
files/file_test.go
files/file_test.go
+5
-5
files/readerfile.go
files/readerfile.go
+26
-6
files/serialfile.go
files/serialfile.go
+37
-2
files/slicefile.go
files/slicefile.go
+42
-7
http/client.go
http/client.go
+11
-1
http/handler.go
http/handler.go
+6
-0
http/multifilereader_test.go
http/multifilereader_test.go
+8
-8
request.go
request.go
+16
-1
response.go
response.go
+37
-5
No files found.
cli/parse.go
View file @
42c9a8a3
...
...
@@ -64,7 +64,7 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
}
req
.
SetArguments
(
stringArgs
)
file
:=
&
files
.
SliceFile
{
""
,
fileArgs
}
file
:=
files
.
New
SliceFile
(
""
,
fileArgs
)
req
.
SetFiles
(
file
)
err
=
cmd
.
CheckArguments
(
req
)
...
...
@@ -298,7 +298,7 @@ func appendFile(args []files.File, inputs []string, argDef *cmds.Argument, recur
}
func
appendStdinAsFile
(
args
[]
files
.
File
,
stdin
*
os
.
File
)
([]
files
.
File
,
*
os
.
File
)
{
arg
:=
&
files
.
ReaderFile
{
""
,
stdin
}
arg
:=
files
.
New
ReaderFile
(
""
,
stdin
,
nil
)
return
append
(
args
,
arg
),
nil
}
...
...
command.go
View file @
42c9a8a3
...
...
@@ -14,7 +14,7 @@ var log = u.Logger("command")
// Function is the type of function that Commands use.
// It reads from the Request, and writes results to the Response.
type
Function
func
(
Request
)
(
interface
{},
error
)
type
Function
func
(
Request
,
Response
)
// Marshaler is a function that takes in a Response, and returns an io.Reader
// (or an error on failure)
...
...
@@ -40,18 +40,14 @@ type HelpText struct {
Subcommands
string
// overrides SUBCOMMANDS section
}
// TODO: check Argument definitions when creating a Command
// (might need to use a Command constructor)
// * make sure any variadic args are at the end
// * make sure there aren't duplicate names
// * make sure optional arguments aren't followed by required arguments
// Command is a runnable command, with input arguments and options (flags).
// It can also have Subcommands, to group units of work into sets.
type
Command
struct
{
Options
[]
Option
Arguments
[]
Argument
PreRun
func
(
req
Request
)
error
Run
Function
PostRun
Function
Marshalers
map
[
EncodingType
]
Marshaler
Helptext
HelpText
...
...
@@ -99,21 +95,12 @@ func (c *Command) Call(req Request) Response {
return
res
}
output
,
err
:=
cmd
.
Run
(
req
)
if
err
!=
nil
{
// if returned error is a commands.Error, use its error code
// otherwise, just default the code to ErrNormal
switch
e
:=
err
.
(
type
)
{
case
*
Error
:
res
.
SetError
(
e
,
e
.
Code
)
case
Error
:
res
.
SetError
(
e
,
e
.
Code
)
default
:
res
.
SetError
(
err
,
ErrNormal
)
}
cmd
.
Run
(
req
,
res
)
if
res
.
Error
()
!=
nil
{
return
res
}
output
:=
res
.
Output
()
isChan
:=
false
actualType
:=
reflect
.
TypeOf
(
output
)
if
actualType
!=
nil
{
...
...
@@ -144,7 +131,6 @@ func (c *Command) Call(req Request) Response {
}
}
res
.
SetOutput
(
output
)
return
res
}
...
...
command_test.go
View file @
42c9a8a3
...
...
@@ -2,8 +2,8 @@ package commands
import
"testing"
func
noop
(
req
Request
)
(
interface
{},
error
)
{
return
nil
,
nil
func
noop
(
req
Request
,
res
Response
)
{
return
}
func
TestOptionValidation
(
t
*
testing
.
T
)
{
...
...
files/file.go
View file @
42c9a8a3
...
...
@@ -3,6 +3,7 @@ package files
import
(
"errors"
"io"
"os"
)
var
(
...
...
@@ -29,3 +30,22 @@ type File interface {
// If the file is a regular file (not a directory), NextFile will return a non-nil error.
NextFile
()
(
File
,
error
)
}
type
StatFile
interface
{
File
Stat
()
os
.
FileInfo
}
type
PeekFile
interface
{
SizeFile
Peek
(
n
int
)
File
Length
()
int
}
type
SizeFile
interface
{
File
Size
()
(
int64
,
error
)
}
files/file_test.go
View file @
42c9a8a3
...
...
@@ -11,13 +11,13 @@ import (
func
TestSliceFiles
(
t
*
testing
.
T
)
{
name
:=
"testname"
files
:=
[]
File
{
&
ReaderFile
{
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"Some text!
\n
"
))
}
,
&
ReaderFile
{
"beep.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"beep"
))
}
,
&
ReaderFile
{
"boop.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"boop"
))
}
,
New
ReaderFile
(
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"Some text!
\n
"
))
,
nil
)
,
New
ReaderFile
(
"beep.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"beep"
))
,
nil
)
,
New
ReaderFile
(
"boop.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"boop"
))
,
nil
)
,
}
buf
:=
make
([]
byte
,
20
)
sf
:=
&
SliceFile
{
name
,
files
}
sf
:=
New
SliceFile
(
name
,
files
)
if
!
sf
.
IsDirectory
()
{
t
.
Error
(
"SliceFile should always be a directory"
)
...
...
@@ -55,7 +55,7 @@ func TestSliceFiles(t *testing.T) {
func
TestReaderFiles
(
t
*
testing
.
T
)
{
message
:=
"beep boop"
rf
:=
&
ReaderFile
{
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
message
))
}
rf
:=
New
ReaderFile
(
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
message
))
,
nil
)
buf
:=
make
([]
byte
,
len
(
message
))
if
rf
.
IsDirectory
()
{
...
...
files/readerfile.go
View file @
42c9a8a3
package
files
import
"io"
import
(
"errors"
"io"
"os"
)
// ReaderFile is a implementation of File created from an `io.Reader`.
// ReaderFiles are never directories, and can be read from and closed.
type
ReaderFile
struct
{
Filename
string
Reader
io
.
ReadCloser
filename
string
reader
io
.
ReadCloser
stat
os
.
FileInfo
}
func
NewReaderFile
(
filename
string
,
reader
io
.
ReadCloser
,
stat
os
.
FileInfo
)
*
ReaderFile
{
return
&
ReaderFile
{
filename
,
reader
,
stat
}
}
func
(
f
*
ReaderFile
)
IsDirectory
()
bool
{
...
...
@@ -18,13 +27,24 @@ func (f *ReaderFile) NextFile() (File, error) {
}
func
(
f
*
ReaderFile
)
FileName
()
string
{
return
f
.
F
ilename
return
f
.
f
ilename
}
func
(
f
*
ReaderFile
)
Read
(
p
[]
byte
)
(
int
,
error
)
{
return
f
.
R
eader
.
Read
(
p
)
return
f
.
r
eader
.
Read
(
p
)
}
func
(
f
*
ReaderFile
)
Close
()
error
{
return
f
.
Reader
.
Close
()
return
f
.
reader
.
Close
()
}
func
(
f
*
ReaderFile
)
Stat
()
os
.
FileInfo
{
return
f
.
stat
}
func
(
f
*
ReaderFile
)
Size
()
(
int64
,
error
)
{
if
f
.
stat
==
nil
{
return
0
,
errors
.
New
(
"File size unknown"
)
}
return
f
.
stat
.
Size
(),
nil
}
files/serialfile.go
View file @
42c9a8a3
...
...
@@ -20,6 +20,7 @@ func (es sortFIByName) Less(i, j int) bool { return es[i].Name() < es[j].Name()
type
serialFile
struct
{
path
string
files
[]
os
.
FileInfo
stat
os
.
FileInfo
current
*
os
.
File
}
...
...
@@ -35,7 +36,7 @@ func NewSerialFile(path string, file *os.File) (File, error) {
func
newSerialFile
(
path
string
,
file
*
os
.
File
,
stat
os
.
FileInfo
)
(
File
,
error
)
{
// for non-directories, return a ReaderFile
if
!
stat
.
IsDir
()
{
return
&
ReaderFile
{
path
,
file
},
nil
return
&
ReaderFile
{
path
,
file
,
stat
},
nil
}
// for directories, stat all of the contents first, so we know what files to
...
...
@@ -55,7 +56,7 @@ func newSerialFile(path string, file *os.File, stat os.FileInfo) (File, error) {
// make sure contents are sorted so -- repeatably -- we get the same inputs.
sort
.
Sort
(
sortFIByName
(
contents
))
return
&
serialFile
{
path
,
contents
,
nil
},
nil
return
&
serialFile
{
path
,
contents
,
stat
,
nil
},
nil
}
func
(
f
*
serialFile
)
IsDirectory
()
bool
{
...
...
@@ -113,3 +114,37 @@ func (f *serialFile) Close() error {
return
nil
}
func
(
f
*
serialFile
)
Stat
()
os
.
FileInfo
{
return
f
.
stat
}
func
(
f
*
serialFile
)
Size
()
(
int64
,
error
)
{
return
size
(
f
.
stat
,
f
.
FileName
())
}
func
size
(
stat
os
.
FileInfo
,
filename
string
)
(
int64
,
error
)
{
if
!
stat
.
IsDir
()
{
return
stat
.
Size
(),
nil
}
file
,
err
:=
os
.
Open
(
filename
)
if
err
!=
nil
{
return
0
,
err
}
files
,
err
:=
file
.
Readdir
(
0
)
if
err
!=
nil
{
return
0
,
err
}
file
.
Close
()
var
output
int64
for
_
,
child
:=
range
files
{
s
,
err
:=
size
(
child
,
fp
.
Join
(
filename
,
child
.
Name
()))
if
err
!=
nil
{
return
0
,
err
}
output
+=
s
}
return
output
,
nil
}
files/slicefile.go
View file @
42c9a8a3
package
files
import
"io"
import
(
"errors"
"io"
)
// SliceFile implements File, and provides simple directory handling.
// It contains children files, and is created from a `[]File`.
// SliceFiles are always directories, and can't be read from or closed.
type
SliceFile
struct
{
Filename
string
Files
[]
File
filename
string
files
[]
File
n
int
}
func
NewSliceFile
(
filename
string
,
files
[]
File
)
*
SliceFile
{
return
&
SliceFile
{
filename
,
files
,
0
}
}
func
(
f
*
SliceFile
)
IsDirectory
()
bool
{
...
...
@@ -15,16 +23,16 @@ func (f *SliceFile) IsDirectory() bool {
}
func
(
f
*
SliceFile
)
NextFile
()
(
File
,
error
)
{
if
len
(
f
.
F
iles
)
==
0
{
if
f
.
n
>=
len
(
f
.
f
iles
)
{
return
nil
,
io
.
EOF
}
file
:=
f
.
F
iles
[
0
]
f
.
Files
=
f
.
Files
[
1
:
]
file
:=
f
.
f
iles
[
f
.
n
]
f
.
n
++
return
file
,
nil
}
func
(
f
*
SliceFile
)
FileName
()
string
{
return
f
.
F
ilename
return
f
.
f
ilename
}
func
(
f
*
SliceFile
)
Read
(
p
[]
byte
)
(
int
,
error
)
{
...
...
@@ -34,3 +42,30 @@ func (f *SliceFile) Read(p []byte) (int, error) {
func
(
f
*
SliceFile
)
Close
()
error
{
return
ErrNotReader
}
func
(
f
*
SliceFile
)
Peek
(
n
int
)
File
{
return
f
.
files
[
n
]
}
func
(
f
*
SliceFile
)
Length
()
int
{
return
len
(
f
.
files
)
}
func
(
f
*
SliceFile
)
Size
()
(
int64
,
error
)
{
var
size
int64
for
_
,
file
:=
range
f
.
files
{
sizeFile
,
ok
:=
file
.
(
SizeFile
)
if
!
ok
{
return
0
,
errors
.
New
(
"Could not get size of child file"
)
}
s
,
err
:=
sizeFile
.
Size
()
if
err
!=
nil
{
return
0
,
err
}
size
+=
s
}
return
size
,
nil
}
http/client.go
View file @
42c9a8a3
...
...
@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
cmds
"github.com/jbenet/go-ipfs/commands"
...
...
@@ -137,9 +138,18 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error
var
err
error
res
:=
cmds
.
NewResponse
(
req
)
contentType
:=
httpRes
.
Header
[
"C
ontent
-
Type
"
][
0
]
contentType
:=
httpRes
.
Header
.
Get
(
c
ontentType
Header
)
contentType
=
strings
.
Split
(
contentType
,
";"
)[
0
]
lengthHeader
:=
httpRes
.
Header
.
Get
(
contentLengthHeader
)
if
len
(
lengthHeader
)
>
0
{
length
,
err
:=
strconv
.
ParseUint
(
lengthHeader
,
10
,
64
)
if
err
!=
nil
{
return
nil
,
err
}
res
.
SetLength
(
length
)
}
if
len
(
httpRes
.
Header
.
Get
(
streamHeader
))
>
0
{
// if output is a stream, we can just use the body reader
res
.
SetOutput
(
httpRes
.
Body
)
...
...
http/handler.go
View file @
42c9a8a3
...
...
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net/http"
"strconv"
context
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
...
...
@@ -92,6 +93,11 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w
.
Header
()
.
Set
(
contentTypeHeader
,
mime
)
}
// set the Content-Length from the response length
if
res
.
Length
()
>
0
{
w
.
Header
()
.
Set
(
contentLengthHeader
,
strconv
.
FormatUint
(
res
.
Length
(),
10
))
}
// if response contains an error, write an HTTP error status code
if
e
:=
res
.
Error
();
e
!=
nil
{
if
e
.
Code
==
cmds
.
ErrClient
{
...
...
http/multifilereader_test.go
View file @
42c9a8a3
...
...
@@ -13,14 +13,14 @@ import (
func
TestOutput
(
t
*
testing
.
T
)
{
text
:=
"Some text! :)"
fileset
:=
[]
files
.
File
{
&
files
.
ReaderFile
{
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
text
))
}
,
&
files
.
SliceFile
{
"boop"
,
[]
files
.
File
{
&
files
.
ReaderFile
{
"boop/a.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"bleep"
))
}
,
&
files
.
ReaderFile
{
"boop/b.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"bloop"
))
}
,
}
}
,
&
files
.
ReaderFile
{
"beep.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"beep"
))
}
,
}
sf
:=
&
files
.
SliceFile
{
""
,
fileset
}
files
.
New
ReaderFile
(
"file.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
text
))
,
nil
)
,
files
.
New
SliceFile
(
"boop"
,
[]
files
.
File
{
files
.
New
ReaderFile
(
"boop/a.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"bleep"
))
,
nil
)
,
files
.
New
ReaderFile
(
"boop/b.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"bloop"
))
,
nil
)
,
}
)
,
files
.
New
ReaderFile
(
"beep.txt"
,
ioutil
.
NopCloser
(
strings
.
NewReader
(
"beep"
))
,
nil
)
,
}
sf
:=
files
.
New
SliceFile
(
""
,
fileset
)
buf
:=
make
([]
byte
,
20
)
// testing output by reading it with the go stdlib "mime/multipart" Reader
...
...
request.go
View file @
42c9a8a3
...
...
@@ -3,6 +3,8 @@ package commands
import
(
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
...
...
@@ -77,6 +79,8 @@ type Request interface {
Context
()
*
Context
SetContext
(
Context
)
Command
()
*
Command
Values
()
map
[
string
]
interface
{}
Stdin
()
io
.
Reader
ConvertOptions
()
error
}
...
...
@@ -89,6 +93,8 @@ type request struct {
cmd
*
Command
ctx
Context
optionDefs
map
[
string
]
Option
values
map
[
string
]
interface
{}
stdin
io
.
Reader
}
// Path returns the command path of this request
...
...
@@ -208,6 +214,14 @@ var converters = map[reflect.Kind]converter{
},
}
func
(
r
*
request
)
Values
()
map
[
string
]
interface
{}
{
return
r
.
values
}
func
(
r
*
request
)
Stdin
()
io
.
Reader
{
return
r
.
stdin
}
func
(
r
*
request
)
ConvertOptions
()
error
{
for
k
,
v
:=
range
r
.
options
{
opt
,
ok
:=
r
.
optionDefs
[
k
]
...
...
@@ -275,7 +289,8 @@ func NewRequest(path []string, opts optMap, args []string, file files.File, cmd
}
ctx
:=
Context
{
Context
:
context
.
TODO
()}
req
:=
&
request
{
path
,
opts
,
args
,
file
,
cmd
,
ctx
,
optDefs
}
values
:=
make
(
map
[
string
]
interface
{})
req
:=
&
request
{
path
,
opts
,
args
,
file
,
cmd
,
ctx
,
optDefs
,
values
,
os
.
Stdin
}
err
:=
req
.
ConvertOptions
()
if
err
!=
nil
{
return
nil
,
err
...
...
response.go
View file @
42c9a8a3
...
...
@@ -6,6 +6,7 @@ import (
"encoding/xml"
"fmt"
"io"
"os"
"strings"
)
...
...
@@ -95,19 +96,30 @@ type Response interface {
SetOutput
(
interface
{})
Output
()
interface
{}
// Sets/Returns the length of the output
SetLength
(
uint64
)
Length
()
uint64
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaler (Codec).
Marshal
()
(
io
.
Reader
,
error
)
// Gets a io.Reader that reads the marshalled output
Reader
()
(
io
.
Reader
,
error
)
// Gets Stdout and Stderr, for writing to console without using SetOutput
Stdout
()
io
.
Writer
Stderr
()
io
.
Writer
}
type
response
struct
{
req
Request
err
*
Error
value
interface
{}
out
io
.
Reader
req
Request
err
*
Error
value
interface
{}
out
io
.
Reader
length
uint64
stdout
io
.
Writer
stderr
io
.
Writer
}
func
(
r
*
response
)
Request
()
Request
{
...
...
@@ -122,6 +134,14 @@ func (r *response) SetOutput(v interface{}) {
r
.
value
=
v
}
func
(
r
*
response
)
Length
()
uint64
{
return
r
.
length
}
func
(
r
*
response
)
SetLength
(
l
uint64
)
{
r
.
length
=
l
}
func
(
r
*
response
)
Error
()
*
Error
{
return
r
.
err
}
...
...
@@ -193,7 +213,19 @@ func (r *response) Reader() (io.Reader, error) {
return
r
.
out
,
nil
}
func
(
r
*
response
)
Stdout
()
io
.
Writer
{
return
r
.
stdout
}
func
(
r
*
response
)
Stderr
()
io
.
Writer
{
return
r
.
stderr
}
// NewResponse returns a response to match given Request
func
NewResponse
(
req
Request
)
Response
{
return
&
response
{
req
:
req
}
return
&
response
{
req
:
req
,
stdout
:
os
.
Stdout
,
stderr
:
os
.
Stderr
,
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment