1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-08-06 03:19:47 +08:00

commands: Changed Marshaler to return a io.Reader instead of a []byte

core/commands: Refactored command marshalers
This commit is contained in:
Matt Bell
2014-12-16 04:31:13 -08:00
parent 0395a7af1e
commit fd40702f73
19 changed files with 136 additions and 85 deletions

View File

@ -3,6 +3,7 @@ package commands
import (
"errors"
"fmt"
"io"
"reflect"
"strings"
@ -15,9 +16,9 @@ var log = u.Logger("command")
// It reads from the Request, and writes results to the Response.
type Function func(Request) (interface{}, error)
// Marshaler is a function that takes in a Response, and returns a marshalled []byte
// Marshaler is a function that takes in a Response, and returns an io.Reader
// (or an error on failure)
type Marshaler func(Response) ([]byte, error)
type Marshaler func(Response) (io.Reader, error)
// MarshalerMap is a map of Marshaler functions, keyed by EncodingType
// (or an error on failure)
@ -113,12 +114,16 @@ func (c *Command) Call(req Request) Response {
return res
}
// If the command specified an output type, ensure the actual value returned is of that type
if cmd.Type != nil {
definedType := reflect.ValueOf(cmd.Type).Type()
actualType := reflect.ValueOf(output).Type()
if definedType != actualType {
// test if output is a channel
isChan := actualType.Kind() == reflect.Chan
// If the command specified an output type, ensure the actual value returned is of that type
if cmd.Type != nil && !isChan {
expectedType := reflect.ValueOf(cmd.Type).Type()
if actualType != expectedType {
res.SetError(ErrIncorrectType, ErrNormal)
return res
}

View File

@ -41,17 +41,33 @@ const (
)
var marshallers = map[EncodingType]Marshaler{
JSON: func(res Response) ([]byte, error) {
JSON: func(res Response) (io.Reader, error) {
var value interface{}
if res.Error() != nil {
return json.MarshalIndent(res.Error(), "", " ")
value = res.Error()
} else {
value = res.Output()
}
return json.MarshalIndent(res.Output(), "", " ")
b, err := json.MarshalIndent(value, "", " ")
if err != nil {
return nil, err
}
return bytes.NewReader(b), nil
},
XML: func(res Response) ([]byte, error) {
XML: func(res Response) (io.Reader, error) {
var value interface{}
if res.Error() != nil {
return xml.Marshal(res.Error())
value = res.Error()
} else {
value = res.Output()
}
return xml.Marshal(res.Output())
b, err := xml.Marshal(value)
if err != nil {
return nil, err
}
return bytes.NewReader(b), nil
},
}
@ -70,7 +86,7 @@ type Response interface {
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaler (Codec).
Marshal() ([]byte, error)
Marshal() (io.Reader, error)
// Gets a io.Reader that reads the marshalled output
Reader() (io.Reader, error)
@ -103,9 +119,9 @@ func (r *response) SetError(err error, code ErrorType) {
r.err = &Error{Message: err.Error(), Code: code}
}
func (r *response) Marshal() ([]byte, error) {
func (r *response) Marshal() (io.Reader, error) {
if r.err == nil && r.value == nil {
return []byte{}, nil
return bytes.NewReader([]byte{}), nil
}
enc, found, err := r.req.Option(EncShort).String()
@ -119,7 +135,7 @@ func (r *response) Marshal() ([]byte, error) {
// Special case: if text encoding and an error, just print it out.
if encType == Text && r.Error() != nil {
return []byte(r.Error().Error()), nil
return strings.NewReader(r.Error().Error()), nil
}
var marshaller Marshaler
@ -140,22 +156,20 @@ func (r *response) Marshal() ([]byte, error) {
// Reader returns an `io.Reader` representing marshalled output of this Response
// Note that multiple calls to this will return a reference to the same io.Reader
func (r *response) Reader() (io.Reader, error) {
// if command set value to a io.Reader, use that as our reader
if r.out == nil {
if out, ok := r.value.(io.Reader); ok {
// if command returned a io.Reader, use that as our reader
r.out = out
}
}
if r.out == nil {
// no reader set, so marshal the error or value
} else {
// otherwise, use the response marshaler output
marshalled, err := r.Marshal()
if err != nil {
return nil, err
}
// create a Reader from the marshalled data
r.out = bytes.NewReader(marshalled)
r.out = marshalled
}
}
return r.out, nil

View File

@ -76,7 +76,7 @@ remains to be implemented.
return added, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
val, ok := res.Output().(*AddOutput)
if !ok {
return nil, u.ErrCast()
@ -93,7 +93,7 @@ remains to be implemented.
buf.Write([]byte(fmt.Sprintf("added %s %s\n", obj.Hash, val.Names[i])))
}
}
return buf.Bytes(), nil
return &buf, nil
},
},
Type: &AddOutput{},

View File

@ -2,7 +2,9 @@ package commands
import (
"bytes"
"io"
"io/ioutil"
"strings"
"time"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
@ -123,9 +125,9 @@ It reads from stdin, and <key> is a base58 encoded multihash.
},
Type: &Block{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
block := res.Output().(*Block)
return []byte(block.Key + "\n"), nil
return strings.NewReader(block.Key + "\n"), nil
},
},
}

View File

@ -114,7 +114,7 @@ in the bootstrap list).
},
Type: &BootstrapOutput{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v, ok := res.Output().(*BootstrapOutput)
if !ok {
return nil, u.ErrCast()
@ -122,7 +122,7 @@ in the bootstrap list).
var buf bytes.Buffer
err := bootstrapWritePeers(&buf, "added ", v.Peers)
return buf.Bytes(), err
return &buf, err
},
},
}
@ -175,7 +175,7 @@ var bootstrapRemoveCmd = &cmds.Command{
},
Type: &BootstrapOutput{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v, ok := res.Output().(*BootstrapOutput)
if !ok {
return nil, u.ErrCast()
@ -183,7 +183,7 @@ var bootstrapRemoveCmd = &cmds.Command{
var buf bytes.Buffer
err := bootstrapWritePeers(&buf, "removed ", v.Peers)
return buf.Bytes(), err
return &buf, err
},
},
}
@ -209,7 +209,7 @@ var bootstrapListCmd = &cmds.Command{
},
}
func bootstrapMarshaler(res cmds.Response) ([]byte, error) {
func bootstrapMarshaler(res cmds.Response) (io.Reader, error) {
v, ok := res.Output().(*BootstrapOutput)
if !ok {
return nil, u.ErrCast()
@ -217,7 +217,7 @@ func bootstrapMarshaler(res cmds.Response) ([]byte, error) {
var buf bytes.Buffer
err := bootstrapWritePeers(&buf, "", v.Peers)
return buf.Bytes(), err
return &buf, err
}
func bootstrapWritePeers(w io.Writer, prefix string, peers []config.BootstrapPeer) error {

View File

@ -2,6 +2,7 @@ package commands
import (
"bytes"
"io"
"sort"
cmds "github.com/jbenet/go-ipfs/commands"
@ -26,13 +27,13 @@ func CommandsCmd(root *cmds.Command) *cmds.Command {
return &root, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*Command)
var buf bytes.Buffer
for _, s := range cmdPathStrings(v) {
buf.Write([]byte(s + "\n"))
}
return buf.Bytes(), nil
return &buf, nil
},
},
Type: &Command{},

View File

@ -72,7 +72,7 @@ Set the value of the 'datastore.path' key:
}
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
if len(res.Request().Arguments()) == 2 {
return nil, nil // dont output anything
}
@ -92,7 +92,7 @@ Set the value of the 'datastore.path' key:
return nil, err
}
buf = append(buf, byte('\n'))
return buf, nil
return bytes.NewReader(buf), nil
},
},
Type: &ConfigField{},

View File

@ -86,7 +86,7 @@ connected peers and latencies between them.
},
Type: &DiagnosticOutput{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(r cmds.Response) ([]byte, error) {
cmds.Text: func(r cmds.Response) (io.Reader, error) {
output, ok := r.Output().(*DiagnosticOutput)
if !ok {
return nil, util.ErrCast()
@ -96,7 +96,7 @@ connected peers and latencies between them.
if err != nil {
return nil, err
}
return buf.Bytes(), nil
return &buf, nil
},
},
}

View File

@ -1,9 +1,11 @@
package commands
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"io"
"time"
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
@ -76,13 +78,17 @@ if no peer is specified, prints out local peers info.
return printPeer(node.Peerstore, p.ID)
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
val, ok := res.Output().(*IdOutput)
if !ok {
return nil, u.ErrCast()
}
return json.MarshalIndent(val, "", "\t")
marshaled, err := json.MarshalIndent(val, "", "\t")
if err != nil {
return nil, err
}
return bytes.NewReader(marshaled), nil
},
},
Type: &IdOutput{},

View File

@ -2,6 +2,8 @@ package commands
import (
"fmt"
"io"
"strings"
cmds "github.com/jbenet/go-ipfs/commands"
merkledag "github.com/jbenet/go-ipfs/merkledag"
@ -70,7 +72,7 @@ it contains, with the following format:
return &LsOutput{output}, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
s := ""
output := res.Output().(*LsOutput).Objects
@ -84,7 +86,7 @@ it contains, with the following format:
}
}
return []byte(s), nil
return strings.NewReader(s), nil
},
},
Type: &LsOutput{},

View File

@ -4,6 +4,7 @@ package commands
import (
"fmt"
"io"
"strings"
"time"
@ -134,11 +135,11 @@ baz
},
Type: &config.Mounts{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*config.Mounts)
s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS)
s += fmt.Sprintf("IPNS mounted at: %s\n", v.IPNS)
return []byte(s), nil
return strings.NewReader(s), nil
},
},
}

View File

@ -6,6 +6,7 @@ import (
"errors"
"io"
"io/ioutil"
"strings"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
@ -101,10 +102,10 @@ multihash.
return objectLinks(n, key)
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
object := res.Output().(*Object)
marshalled := marshalLinks(object.Links)
return []byte(marshalled), nil
return strings.NewReader(marshalled), nil
},
},
Type: &Object{},
@ -163,13 +164,18 @@ This command outputs data in the following encodings:
},
Type: &Node{},
Marshalers: cmds.MarshalerMap{
cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) {
cmds.EncodingType("protobuf"): func(res cmds.Response) (io.Reader, error) {
node := res.Output().(*Node)
object, err := deserializeNode(node)
if err != nil {
return nil, err
}
return object.Marshal()
marshaled, err := object.Marshal()
if err != nil {
return nil, err
}
return bytes.NewReader(marshaled), nil
},
},
}
@ -221,9 +227,9 @@ Data should be in the format specified by <encoding>.
return output, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
object := res.Output().(*Object)
return []byte("added " + object.Hash), nil
return strings.NewReader("added " + object.Hash), nil
},
},
Type: &Object{},

View File

@ -3,6 +3,8 @@ package commands
import (
"errors"
"fmt"
"io"
"strings"
cmds "github.com/jbenet/go-ipfs/commands"
core "github.com/jbenet/go-ipfs/core"
@ -78,10 +80,10 @@ Publish a <ref> to another public key:
return publish(n, n.PrivateKey, ref)
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*IpnsEntry)
s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value)
return []byte(s), nil
return strings.NewReader(s), nil
},
},
Type: &IpnsEntry{},

View File

@ -1,7 +1,9 @@
package commands
import (
"bytes"
"fmt"
"io"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
cmds "github.com/jbenet/go-ipfs/commands"
@ -16,13 +18,13 @@ type KeyList struct {
}
// KeyListTextMarshaler outputs a KeyList as plaintext, one key per line
func KeyListTextMarshaler(res cmds.Response) ([]byte, error) {
func KeyListTextMarshaler(res cmds.Response) (io.Reader, error) {
output := res.Output().(*KeyList)
s := ""
var buf bytes.Buffer
for _, key := range output.Keys {
s += key.B58String() + "\n"
buf.WriteString(key.B58String() + "\n")
}
return []byte(s), nil
return &buf, nil
}
var RefsCmd = &cmds.Command{

View File

@ -2,6 +2,8 @@ package commands
import (
"errors"
"io"
"strings"
cmds "github.com/jbenet/go-ipfs/commands"
)
@ -71,9 +73,9 @@ Resolve te value of another name:
return output, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
output := res.Output().(string)
return []byte(output), nil
return strings.NewReader(output), nil
},
},
}

View File

@ -1,6 +1,9 @@
package commands
import (
"io"
"strings"
cmds "github.com/jbenet/go-ipfs/commands"
u "github.com/jbenet/go-ipfs/util"
)
@ -90,6 +93,6 @@ type MessageOutput struct {
Message string
}
func MessageTextMarshaler(res cmds.Response) ([]byte, error) {
return []byte(res.Output().(*MessageOutput).Message), nil
func MessageTextMarshaler(res cmds.Response) (io.Reader, error) {
return strings.NewReader(res.Output().(*MessageOutput).Message), nil
}

View File

@ -3,6 +3,7 @@ package commands
import (
"bytes"
"fmt"
"io"
"path"
cmds "github.com/jbenet/go-ipfs/commands"
@ -124,7 +125,7 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8Szb
Type: &stringList{},
}
func stringListMarshaler(res cmds.Response) ([]byte, error) {
func stringListMarshaler(res cmds.Response) (io.Reader, error) {
list, ok := res.Output().(*stringList)
if !ok {
return nil, errors.New("failed to cast []string")
@ -132,10 +133,10 @@ func stringListMarshaler(res cmds.Response) ([]byte, error) {
var buf bytes.Buffer
for _, s := range list.Strings {
buf.Write([]byte(s))
buf.Write([]byte("\n"))
buf.WriteString(s)
buf.WriteString("\n")
}
return buf.Bytes(), nil
return &buf, nil
}
// splitAddresses is a function that takes in a slice of string peer addresses

View File

@ -1,8 +1,10 @@
package commands
import (
"bytes"
"errors"
"fmt"
"io"
cmds "github.com/jbenet/go-ipfs/commands"
"github.com/jbenet/go-ipfs/core"
@ -33,16 +35,16 @@ var UpdateCmd = &cmds.Command{
"log": UpdateLogCmd,
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*UpdateOutput)
s := ""
var buf bytes.Buffer
if v.NewVersion != v.OldVersion {
s = fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')\n",
v.NewVersion, v.OldVersion)
buf.WriteString(fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')\n",
v.NewVersion, v.OldVersion))
} else {
s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion)
buf.WriteString(fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion))
}
return []byte(s), nil
return &buf, nil
},
},
}
@ -62,16 +64,16 @@ var UpdateCheckCmd = &cmds.Command{
},
Type: &UpdateOutput{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*UpdateOutput)
s := ""
var buf bytes.Buffer
if v.NewVersion != v.OldVersion {
s = fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')\n",
v.NewVersion, v.OldVersion)
buf.WriteString(fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')\n",
v.NewVersion, v.OldVersion))
} else {
s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion)
buf.WriteString(fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion))
}
return []byte(s), nil
return &buf, nil
},
},
}

View File

@ -2,6 +2,8 @@ package commands
import (
"fmt"
"io"
"strings"
cmds "github.com/jbenet/go-ipfs/commands"
config "github.com/jbenet/go-ipfs/config"
@ -26,7 +28,7 @@ var VersionCmd = &cmds.Command{
}, nil
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) ([]byte, error) {
cmds.Text: func(res cmds.Response) (io.Reader, error) {
v := res.Output().(*VersionOutput)
number, found, err := res.Request().Option("number").Bool()
@ -34,9 +36,9 @@ var VersionCmd = &cmds.Command{
return nil, err
}
if found && number {
return []byte(fmt.Sprintln(v.Version)), nil
return strings.NewReader(fmt.Sprintln(v.Version)), nil
}
return []byte(fmt.Sprintf("ipfs version %s\n", v.Version)), nil
return strings.NewReader(fmt.Sprintf("ipfs version %s\n", v.Version)), nil
},
},
Type: &VersionOutput{},