Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

forward the remaining of the stdin args to the server #83

Merged
merged 4 commits into from
Mar 18, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cmds
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to be able to continue treating the argument reader as an io.ReadCloser so we can't use bufio.Scanner.


import (
"bufio"
"io"
)

// StdinArguments is used to iterate through arguments piped through stdin.
type StdinArguments interface {
io.ReadCloser
// Returns the next argument passed via stdin.
//
// This method will never return an error along with a value, it will
// return one or the other.
//
// Once all arguments have been read, it will return "", io.EOF
Next() (string, error)
}

type arguments struct {
reader *bufio.Reader
closer io.Closer
}

func newArguments(r io.ReadCloser) *arguments {
return &arguments{
reader: bufio.NewReader(r),
closer: r,
}
}

// Read implements the io.Reader interface
func (a *arguments) Read(b []byte) (int, error) {
return a.reader.Read(b)
}

// Close implements the io.Closer interface
func (a *arguments) Close() error {
return a.closer.Close()
}

// WriteTo implements the io.WriterTo interface
func (a *arguments) WriteTo(w io.Writer) (int64, error) {
return a.reader.WriteTo(w)
}

// Next returns the next argument
func (a *arguments) Next() (string, error) {
s, err := a.reader.ReadString('\n')
switch err {
case io.EOF:
if s == "" {
return "", io.EOF
}
// drop the error.
return s, nil
case nil:
l := len(s)
if l >= 2 && s[l-2] == '\r' {
return s[:l-2], nil
}
return s[:l-1], nil
default:
return "", err
}
}
10 changes: 8 additions & 2 deletions cli/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,14 @@ func TestBodyArgs(t *testing.T) {
}

var bodyArgs words
for s.Scan() {
bodyArgs = append(bodyArgs, s.Text())
for {
next, err := s.Next()
if err == io.EOF {
break
} else if err != nil {
t.Fatal(err)
}
bodyArgs = append(bodyArgs, next)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface is a bit awkward but I'm not a fan of the scanner interface either.

}

if !sameWords(bodyArgs, tc.varArgs) {
Expand Down
11 changes: 5 additions & 6 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ output to the user, including text, JSON, and XML marshallers.
package cmds

import (
"bufio"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -234,7 +233,7 @@ func (c *Command) CheckArguments(req *Request) error {
switch err {
case io.EOF:
case nil:
req.bodyArgs = bufio.NewScanner(fi)
req.bodyArgs = newArguments(fi)
// Can't pass files and stdin arguments.
req.Files = nil
default:
Expand Down Expand Up @@ -267,13 +266,13 @@ func (c *Command) CheckArguments(req *Request) error {

// Can we get it from stdin?
if argDef.SupportsStdin && req.bodyArgs != nil {
if req.bodyArgs.Scan() {
next, err := req.bodyArgs.Next()
if err == nil {
// Found it!
req.Arguments = append(req.Arguments, req.bodyArgs.Text())
req.Arguments = append(req.Arguments, next)
continue
}
// Nope! Maybe we had a read error?
if err := req.bodyArgs.Err(); err != nil {
if err != io.EOF {
return err
}
// No, just missing.
Expand Down
10 changes: 8 additions & 2 deletions http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,14 @@ func (c *client) Send(req *cmds.Request) (cmds.Response, error) {

var fileReader *files.MultiFileReader
var reader io.Reader

if req.Files != nil {
if bodyArgs := req.BodyArgs(); bodyArgs != nil {
// In the end, this wraps a file reader in a file reader.
// However, such is life.
fileReader = files.NewMultiFileReader(files.NewSliceFile("", "", []files.File{
files.NewReaderFile("stdin", "", bodyArgs, nil),
}), true)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah....

reader = fileReader
} else if req.Files != nil {
fileReader = files.NewMultiFileReader(req.Files, true)
reader = fileReader
}
Expand Down
30 changes: 22 additions & 8 deletions request.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cmds

import (
"bufio"
"context"
"fmt"
"io"
"reflect"

"github.com/ipfs/go-ipfs-cmdkit"
Expand All @@ -21,7 +21,7 @@ type Request struct {

Files files.File

bodyArgs *bufio.Scanner
bodyArgs *arguments
}

// NewRequest returns a request initialized with given arguments
Expand Down Expand Up @@ -50,8 +50,17 @@ func NewRequest(ctx context.Context, path []string, opts cmdkit.OptMap, args []s
}

// BodyArgs returns a scanner that returns arguments passed in the body as tokens.
func (req *Request) BodyArgs() *bufio.Scanner {
return req.bodyArgs
//
// Returns nil if there are no arguments to be consumed via stdin.
func (req *Request) BodyArgs() StdinArguments {
// dance to make sure we return an *untyped* nil.
// DO NOT just return `req.bodyArgs`.
// If you'd like to complain, go to
// https://github.com/golang/go/issues/.
if req.bodyArgs != nil {
return req.bodyArgs
}
return nil
}

func (req *Request) ParseBodyArgs() error {
Expand All @@ -60,11 +69,16 @@ func (req *Request) ParseBodyArgs() error {
return nil
}

for s.Scan() {
req.Arguments = append(req.Arguments, s.Text())
for {
next, err := s.Next()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
req.Arguments = append(req.Arguments, next)
}

return s.Err()
}

func (req *Request) SetOption(name string, value interface{}) {
Expand Down