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

commands: Changed option accessor API (Request#Option now returns an OptionValue)

This commit is contained in:
Matt Bell
2014-11-10 21:30:42 -08:00
committed by Juan Batiz-Benet
parent 20591c7e64
commit 3e507f7c9f
14 changed files with 182 additions and 169 deletions

View File

@ -20,30 +20,28 @@ var initCmd = &cmds.Command{
`, `,
Options: []cmds.Option{ Options: []cmds.Option{
cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), cmds.UintOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"),
cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"), cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"),
cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"), cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"),
cmds.StringOption("datastore", "d", "Location for the IPFS data store"), cmds.StringOption("datastore", "d", "Location for the IPFS data store"),
}, },
Run: func(req cmds.Request) (interface{}, error) { Run: func(req cmds.Request) (interface{}, error) {
arg, found := req.Option("d") dspath, err := req.Option("d").String()
dspath, ok := arg.(string) if err != nil {
if found && !ok { return nil, err
return nil, errors.New("failed to parse datastore flag")
} }
arg, found = req.Option("f") force, err := req.Option("f").Bool()
force, ok := arg.(bool) // TODO param if err != nil {
if found && !ok { return nil, err
return nil, errors.New("failed to parse force flag")
} }
arg, found = req.Option("b") nBitsForKeypair, err := req.Option("b").Int()
nBitsForKeypair, ok := arg.(int) // TODO param if err != nil {
if found && !ok { return nil, err
return nil, errors.New("failed to get bits flag") }
} else if !found { if !req.Option("b").Found() {
nBitsForKeypair = 4096 nBitsForKeypair = 4096
} }

View File

@ -66,10 +66,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) {
// or if a path was returned (user specified a valid subcommand), show the error message // or if a path was returned (user specified a valid subcommand), show the error message
// (this means there was an option or argument error) // (this means there was an option or argument error)
if path != nil && len(path) > 0 { if path != nil && len(path) > 0 {
help := false help, _ := req.Option("help").Bool()
opt, _ := req.Option("help")
help, _ = opt.(bool)
if !help { if !help {
fmt.Printf(errorFormat, err) fmt.Printf(errorFormat, err)
} }
@ -108,7 +105,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) {
ctx.ConfigRoot = configPath ctx.ConfigRoot = configPath
ctx.Config = conf ctx.Config = conf
if _, found := req.Option("encoding"); !found { if !req.Option("encoding").Found() {
if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
req.SetOption("encoding", cmds.Text) req.SetOption("encoding", cmds.Text)
} else { } else {
@ -120,30 +117,25 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) {
} }
func handleOptions(req cmds.Request, root *cmds.Command) { func handleOptions(req cmds.Request, root *cmds.Command) {
if help, found := req.Option("help"); found { if help, err := req.Option("help").Bool(); help && err == nil {
if helpBool, ok := help.(bool); helpBool && ok { helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) if err != nil {
if err != nil { fmt.Println(err.Error())
fmt.Println(err.Error()) } else {
} else { fmt.Println(helpText)
fmt.Println(helpText)
}
exit(0)
} else if !ok {
fmt.Println("error: expected 'help' option to be a bool")
exit(1)
} }
exit(0)
} else if err != nil {
fmt.Println(err)
exit(1)
} }
if debug, found := req.Option("debug"); found { if debug, err := req.Option("debug").Bool(); debug && err == nil {
if debugBool, ok := debug.(bool); debugBool && ok { u.Debug = true
u.Debug = true u.SetAllLoggers(logging.DEBUG)
} else if err != nil {
u.SetAllLoggers(logging.DEBUG) fmt.Println(err)
} else if !ok { exit(1)
fmt.Println("error: expected 'debug' option to be a bool")
exit(1)
}
} }
} }
@ -154,19 +146,13 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response {
res = root.Call(req) res = root.Call(req)
} else { } else {
var found bool local, err := req.Option("local").Bool()
var local interface{} if err != nil {
localBool := false fmt.Println(err)
if local, found = req.Option("local"); found { exit(1)
var ok bool
localBool, ok = local.(bool)
if !ok {
fmt.Println("error: expected 'local' option to be a bool")
exit(1)
}
} }
if (!found || !localBool) && daemon.Locked(req.Context().ConfigRoot) { if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) {
addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -229,12 +215,12 @@ func outputResponse(res cmds.Response, root *cmds.Command) {
} }
func getConfigRoot(req cmds.Request) (string, error) { func getConfigRoot(req cmds.Request) (string, error) {
if opt, found := req.Option("config"); found { configOpt, err := req.Option("config").String()
if optStr, ok := opt.(string); ok { if err != nil {
return optStr, nil return "", err
} else { }
return "", fmt.Errorf("Expected 'config' option to be a string") if configOpt != "" {
} return configOpt, nil
} }
configPath, err := config.PathRoot() configPath, err := config.PathRoot()

View File

@ -66,13 +66,7 @@ func (c *Command) Call(req Request) Response {
return res return res
} }
options, err := c.GetOptions(req.Path()) err = req.ConvertOptions()
if err != nil {
res.SetError(err, ErrClient)
return res
}
err = req.ConvertOptions(options)
if err != nil { if err != nil {
res.SetError(err, ErrClient) res.SetError(err, ErrClient)
return res return res

View File

@ -15,29 +15,23 @@ func TestOptionValidation(t *testing.T) {
Run: noop, Run: noop,
} }
req := NewEmptyRequest() opts, _ := cmd.GetOptions(nil)
req.SetOption("beep", 5)
req.SetOption("b", 10)
res := cmd.Call(req)
if res.Error() == nil {
t.Error("Should have failed (duplicate options)")
}
req = NewEmptyRequest() req := NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", "foo") req.SetOption("beep", true)
res = cmd.Call(req) res := cmd.Call(req)
if res.Error() == nil { if res.Error() == nil {
t.Error("Should have failed (incorrect type)") t.Error("Should have failed (incorrect type)")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", 5) req.SetOption("beep", 5)
res = cmd.Call(req) res = cmd.Call(req)
if res.Error() != nil { if res.Error() != nil {
t.Error(res.Error(), "Should have passed") t.Error(res.Error(), "Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", 5) req.SetOption("beep", 5)
req.SetOption("boop", "test") req.SetOption("boop", "test")
res = cmd.Call(req) res = cmd.Call(req)
@ -45,7 +39,7 @@ func TestOptionValidation(t *testing.T) {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", 5) req.SetOption("b", 5)
req.SetOption("B", "test") req.SetOption("B", "test")
res = cmd.Call(req) res = cmd.Call(req)
@ -53,32 +47,32 @@ func TestOptionValidation(t *testing.T) {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("foo", 5) req.SetOption("foo", 5)
res = cmd.Call(req) res = cmd.Call(req)
if res.Error() != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption(EncShort, "json") req.SetOption(EncShort, "json")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error() != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", "100") req.SetOption("b", "100")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error() != nil { if res.Error() != nil {
t.Error("Should have passed") t.Error("Should have passed")
} }
req = NewEmptyRequest() req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", ":)") req.SetOption("b", ":)")
res = cmd.Call(req) res = cmd.Call(req)
if res.Error() == nil { if res.Error() == nil {
t.Error(res.Error(), "Should have failed (string value not convertible to int)") t.Error("Should have failed (string value not convertible to int)")
} }
} }
@ -107,13 +101,14 @@ func TestRegistration(t *testing.T) {
Run: noop, Run: noop,
} }
res := cmdB.Call(NewRequest([]string{"a"}, nil, nil, nil, nil)) path := []string{"a"}
if res.Error() == nil { _, err := cmdB.GetOptions(path)
if err == nil {
t.Error("Should have failed (option name collision)") t.Error("Should have failed (option name collision)")
} }
res = cmdC.Call(NewEmptyRequest()) _, err = cmdC.GetOptions(nil)
if res.Error() == nil { if err == nil {
t.Error("Should have failed (option name collision with global options)") t.Error("Should have failed (option name collision with global options)")
} }
} }

View File

@ -34,23 +34,8 @@ func NewClient(address string) Client {
} }
func (c *client) Send(req cmds.Request) (cmds.Response, error) { func (c *client) Send(req cmds.Request) (cmds.Response, error) {
var userEncoding string userEncoding, _ := req.Option(cmds.EncShort).String()
if enc, found := req.Option(cmds.EncShort); found { req.SetOption(cmds.EncShort, cmds.JSON)
var ok bool
userEncoding, ok = enc.(string)
if !ok {
return nil, castError
}
req.SetOption(cmds.EncShort, cmds.JSON)
} else {
var ok bool
enc, _ := req.Option(cmds.EncLong)
userEncoding, ok = enc.(string)
if !ok {
return nil, castError
}
req.SetOption(cmds.EncLong, cmds.JSON)
}
query, inputStream, err := getQuery(req) query, inputStream, err := getQuery(req)
if err != nil { if err != nil {
@ -72,7 +57,6 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) {
if len(userEncoding) > 0 { if len(userEncoding) > 0 {
req.SetOption(cmds.EncShort, userEncoding) req.SetOption(cmds.EncShort, userEncoding)
req.SetOption(cmds.EncLong, userEncoding)
} }
return res, nil return res, nil

View File

@ -57,13 +57,12 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set(streamHeader, "1") w.Header().Set(streamHeader, "1")
} else { } else {
enc, _ := req.Option(cmds.EncShort) enc, err := req.Option(cmds.EncShort).String()
encStr, ok := enc.(string) if err != nil || len(enc) == 0 {
if !ok {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
mime := mimeTypes[encStr] mime := mimeTypes[enc]
w.Header().Set("Content-Type", mime) w.Header().Set("Content-Type", mime)
} }

View File

@ -1,6 +1,9 @@
package commands package commands
import "reflect" import (
"errors"
"reflect"
)
// Types of Command options // Types of Command options
const ( const (
@ -30,7 +33,7 @@ func NewOption(kind reflect.Kind, names ...string) Option {
} }
desc := names[len(names)-1] desc := names[len(names)-1]
names = names[:len(names)-2] names = names[:len(names)-1]
return Option{ return Option{
Names: names, Names: names,
@ -55,6 +58,73 @@ func StringOption(names ...string) Option {
return NewOption(String, names...) return NewOption(String, names...)
} }
type OptionValue struct {
value interface{}
found bool
}
// Found returns true if the option value was provided by the user (not a default value)
func (ov OptionValue) Found() bool {
return ov.found
}
// value accessor methods, gets the value as a certain type
func (ov OptionValue) Bool() (bool, error) {
val, ok := ov.value.(bool)
if !ok {
var err error
if ov.value != nil {
err = errors.New("error casting to bool")
}
return false, err
}
return val, nil
}
func (ov OptionValue) Int() (int, error) {
val, ok := ov.value.(int)
if !ok {
var err error
if ov.value != nil {
err = errors.New("error casting to int")
}
return 0, err
}
return val, nil
}
func (ov OptionValue) Uint() (uint, error) {
val, ok := ov.value.(uint)
if !ok {
var err error
if ov.value != nil {
err = errors.New("error casting to uint")
}
return 0, err
}
return val, nil
}
func (ov OptionValue) Float() (float64, error) {
val, ok := ov.value.(float64)
if !ok {
var err error
if ov.value != nil {
err = errors.New("error casting to float64")
}
return 0.0, err
}
return val, nil
}
func (ov OptionValue) String() (string, error) {
val, ok := ov.value.(string)
if !ok {
var err error
if ov.value != nil {
err = errors.New("error casting to string")
}
return "", err
}
return val, nil
}
// Flag names // Flag names
const ( const (
EncShort = "enc" EncShort = "enc"

View File

@ -21,15 +21,15 @@ type Context struct {
// Request represents a call to a command from a consumer // Request represents a call to a command from a consumer
type Request interface { type Request interface {
Path() []string Path() []string
Option(name string) (interface{}, bool) Option(name string) *OptionValue
Options() map[string]interface{} Options() optMap
SetOption(name string, val interface{}) SetOption(name string, val interface{})
Arguments() []interface{} // TODO: make argument value type instead of using interface{} Arguments() []interface{} // TODO: make argument value type instead of using interface{}
Context() *Context Context() *Context
SetContext(Context) SetContext(Context)
Command() *Command Command() *Command
ConvertOptions(options map[string]Option) error ConvertOptions() error
} }
type request struct { type request struct {
@ -47,31 +47,34 @@ func (r *request) Path() []string {
} }
// Option returns the value of the option for given name. // Option returns the value of the option for given name.
func (r *request) Option(name string) (interface{}, bool) { func (r *request) Option(name string) *OptionValue {
val, found := r.options[name] val, found := r.options[name]
if found { if found {
return val, found return &OptionValue{val, found}
} }
// if a value isn't defined for that name, we will try to look it up by its aliases // if a value isn't defined for that name, we will try to look it up by its aliases
// find the option with the specified name // find the option with the specified name
option, found := r.optionDefs[name] option, found := r.optionDefs[name]
if found { if !found {
// try all the possible names, break if we find a value return nil
for _, n := range option.Names { }
val, found := r.options[n]
if found { // try all the possible names, break if we find a value
return val, found for _, n := range option.Names {
} val, found = r.options[n]
if found {
return &OptionValue{val, found}
} }
} }
return nil, false // MAYBE_TODO: use default value instead of nil
return &OptionValue{nil, false}
} }
// Options returns a copy of the option map // Options returns a copy of the option map
func (r *request) Options() map[string]interface{} { func (r *request) Options() optMap {
output := make(optMap) output := make(optMap)
for k, v := range r.options { for k, v := range r.options {
output[k] = v output[k] = v
@ -136,11 +139,11 @@ var converters = map[reflect.Kind]converter{
}, },
} }
func (r *request) ConvertOptions(options map[string]Option) error { func (r *request) ConvertOptions() error {
converted := make(map[string]interface{}) converted := make(map[string]interface{})
for k, v := range r.options { for k, v := range r.options {
opt, ok := options[k] opt, ok := r.optionDefs[k]
if !ok { if !ok {
continue continue
} }
@ -203,5 +206,9 @@ func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command, op
if optDefs == nil { if optDefs == nil {
optDefs = make(map[string]Option) optDefs = make(map[string]Option)
} }
return &request{path, opts, args, cmd, Context{}, optDefs}
req := &request{path, opts, args, cmd, Context{}, optDefs}
req.ConvertOptions()
return req
} }

View File

@ -108,18 +108,22 @@ func (r *response) Marshal() ([]byte, error) {
return []byte{}, nil return []byte{}, nil
} }
enc, found := r.req.Option(EncShort) fmt.Println(r.req, r.req.Option(EncShort))
encStr, ok := enc.(string) if !r.req.Option(EncShort).Found() {
if !found || !ok || encStr == "" {
return nil, fmt.Errorf("No encoding type was specified") return nil, fmt.Errorf("No encoding type was specified")
} }
encType := EncodingType(strings.ToLower(encStr)) enc, err := r.req.Option(EncShort).String()
if err != nil {
return nil, err
}
encType := EncodingType(strings.ToLower(enc))
var marshaller Marshaller var marshaller Marshaller
if r.req.Command() != nil && r.req.Command().Marshallers != nil { if r.req.Command() != nil && r.req.Command().Marshallers != nil {
marshaller = r.req.Command().Marshallers[encType] marshaller = r.req.Command().Marshallers[encType]
} }
if marshaller == nil { if marshaller == nil {
var ok bool
marshaller, ok = marshallers[encType] marshaller, ok = marshallers[encType]
if !ok { if !ok {
return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc) return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc)

View File

@ -12,25 +12,20 @@ type TestOutput struct {
} }
func TestMarshalling(t *testing.T) { func TestMarshalling(t *testing.T) {
req := NewEmptyRequest() cmd := &Command{}
opts, _ := cmd.GetOptions(nil)
req := NewRequest(nil, nil, nil, nil, opts)
res := NewResponse(req) res := NewResponse(req)
res.SetOutput(TestOutput{"beep", "boop", 1337}) res.SetOutput(TestOutput{"beep", "boop", 1337})
// get command global options so we can set the encoding option _, err := res.Marshal()
cmd := Command{}
options, err := cmd.GetOptions(nil)
if err != nil {
t.Error(err)
}
_, err = res.Marshal()
if err == nil { if err == nil {
t.Error("Should have failed (no encoding type specified in request)") t.Error("Should have failed (no encoding type specified in request)")
} }
req.SetOption(EncShort, JSON) req.SetOption(EncShort, JSON)
req.ConvertOptions(options)
bytes, err := res.Marshal() bytes, err := res.Marshal()
if err != nil { if err != nil {

View File

@ -44,17 +44,15 @@ not be listable, as it is virtual. Accessing known paths directly.
// update fsdir with flag. // update fsdir with flag.
fsdir := ctx.Config.Mounts.IPFS fsdir := ctx.Config.Mounts.IPFS
opt, _ := req.Option("f") if req.Option("f").Found() {
if val, ok := opt.(string); ok && val != "" { fsdir, _ = req.Option("f").String()
fsdir = val
} }
fsdone := mountIpfs(ctx.Node, fsdir) fsdone := mountIpfs(ctx.Node, fsdir)
// get default mount points // get default mount points
nsdir := ctx.Config.Mounts.IPNS nsdir := ctx.Config.Mounts.IPNS
opt, _ = req.Option("f") if req.Option("n").Found() {
if val, ok := opt.(string); ok && val != "" { nsdir, _ = req.Option("n").String()
nsdir = val
} }
nsdone := mountIpns(ctx.Node, nsdir, fsdir) nsdone := mountIpns(ctx.Node, nsdir, fsdir)

View File

@ -35,8 +35,7 @@ on disk.
n := req.Context().Node n := req.Context().Node
// set recursive flag // set recursive flag
opt, _ := req.Option("recursive") recursive, _ := req.Option("recursive").Bool() // false if cast fails.
recursive, _ := opt.(bool) // false if cast fails.
paths, err := internal.CastToStrings(req.Arguments()) paths, err := internal.CastToStrings(req.Arguments())
if err != nil { if err != nil {
@ -70,8 +69,7 @@ collected if needed.
n := req.Context().Node n := req.Context().Node
// set recursive flag // set recursive flag
opt, _ := req.Option("recursive") recursive, _ := req.Option("recursive").Bool() // false if cast fails.
recursive, _ := opt.(bool) // false if cast fails.
paths, err := internal.CastToStrings(req.Arguments()) paths, err := internal.CastToStrings(req.Arguments())
if err != nil { if err != nil {

View File

@ -35,17 +35,8 @@ Note: list all refs recursively with -r.`,
Run: func(req cmds.Request) (interface{}, error) { Run: func(req cmds.Request) (interface{}, error) {
n := req.Context().Node n := req.Context().Node
opt, found := req.Option("unique") unique, _ := req.Option("unique").Bool()
unique, ok := opt.(bool) recursive, _ := req.Option("recursive").Bool()
if !ok && found {
unique = false
}
opt, found = req.Option("recursive")
recursive, ok := opt.(bool)
if !ok && found {
recursive = false
}
paths, err := internal.CastToStrings(req.Arguments()) paths, err := internal.CastToStrings(req.Arguments())
if err != nil { if err != nil {

View File

@ -1,8 +1,6 @@
package commands package commands
import ( import (
"errors"
cmds "github.com/jbenet/go-ipfs/commands" cmds "github.com/jbenet/go-ipfs/commands"
config "github.com/jbenet/go-ipfs/config" config "github.com/jbenet/go-ipfs/config"
) )
@ -29,11 +27,7 @@ var versionCmd = &cmds.Command{
v := res.Output().(*VersionOutput) v := res.Output().(*VersionOutput)
s := "" s := ""
opt, found := res.Request().Option("number") number, _ := res.Request().Option("number").Bool()
number, ok := opt.(bool)
if found && !ok {
return nil, errors.New("cast error")
}
if !number { if !number {
s += "ipfs version " s += "ipfs version "