1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-06-29 01:12:24 +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{
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.BoolOption("force", "f", "Overwrite existing config (if it exists)"),
cmds.StringOption("datastore", "d", "Location for the IPFS data store"),
},
Run: func(req cmds.Request) (interface{}, error) {
arg, found := req.Option("d")
dspath, ok := arg.(string)
if found && !ok {
return nil, errors.New("failed to parse datastore flag")
dspath, err := req.Option("d").String()
if err != nil {
return nil, err
}
arg, found = req.Option("f")
force, ok := arg.(bool) // TODO param
if found && !ok {
return nil, errors.New("failed to parse force flag")
force, err := req.Option("f").Bool()
if err != nil {
return nil, err
}
arg, found = req.Option("b")
nBitsForKeypair, ok := arg.(int) // TODO param
if found && !ok {
return nil, errors.New("failed to get bits flag")
} else if !found {
nBitsForKeypair, err := req.Option("b").Int()
if err != nil {
return nil, err
}
if !req.Option("b").Found() {
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
// (this means there was an option or argument error)
if path != nil && len(path) > 0 {
help := false
opt, _ := req.Option("help")
help, _ = opt.(bool)
help, _ := req.Option("help").Bool()
if !help {
fmt.Printf(errorFormat, err)
}
@ -108,7 +105,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) {
ctx.ConfigRoot = configPath
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 {
req.SetOption("encoding", cmds.Text)
} else {
@ -120,30 +117,25 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) {
}
func handleOptions(req cmds.Request, root *cmds.Command) {
if help, found := req.Option("help"); found {
if helpBool, ok := help.(bool); helpBool && ok {
helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(helpText)
}
exit(0)
} else if !ok {
fmt.Println("error: expected 'help' option to be a bool")
exit(1)
if help, err := req.Option("help").Bool(); help && err == nil {
helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(helpText)
}
exit(0)
} else if err != nil {
fmt.Println(err)
exit(1)
}
if debug, found := req.Option("debug"); found {
if debugBool, ok := debug.(bool); debugBool && ok {
u.Debug = true
u.SetAllLoggers(logging.DEBUG)
} else if !ok {
fmt.Println("error: expected 'debug' option to be a bool")
exit(1)
}
if debug, err := req.Option("debug").Bool(); debug && err == nil {
u.Debug = true
u.SetAllLoggers(logging.DEBUG)
} else if err != nil {
fmt.Println(err)
exit(1)
}
}
@ -154,19 +146,13 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response {
res = root.Call(req)
} else {
var found bool
var local interface{}
localBool := false
if local, found = req.Option("local"); found {
var ok bool
localBool, ok = local.(bool)
if !ok {
fmt.Println("error: expected 'local' option to be a bool")
exit(1)
}
local, err := req.Option("local").Bool()
if err != nil {
fmt.Println(err)
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)
if err != nil {
fmt.Println(err)
@ -229,12 +215,12 @@ func outputResponse(res cmds.Response, root *cmds.Command) {
}
func getConfigRoot(req cmds.Request) (string, error) {
if opt, found := req.Option("config"); found {
if optStr, ok := opt.(string); ok {
return optStr, nil
} else {
return "", fmt.Errorf("Expected 'config' option to be a string")
}
configOpt, err := req.Option("config").String()
if err != nil {
return "", err
}
if configOpt != "" {
return configOpt, nil
}
configPath, err := config.PathRoot()

View File

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

View File

@ -15,29 +15,23 @@ func TestOptionValidation(t *testing.T) {
Run: noop,
}
req := NewEmptyRequest()
req.SetOption("beep", 5)
req.SetOption("b", 10)
res := cmd.Call(req)
if res.Error() == nil {
t.Error("Should have failed (duplicate options)")
}
opts, _ := cmd.GetOptions(nil)
req = NewEmptyRequest()
req.SetOption("beep", "foo")
res = cmd.Call(req)
req := NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", true)
res := cmd.Call(req)
if res.Error() == nil {
t.Error("Should have failed (incorrect type)")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", 5)
res = cmd.Call(req)
if res.Error() != nil {
t.Error(res.Error(), "Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("beep", 5)
req.SetOption("boop", "test")
res = cmd.Call(req)
@ -45,7 +39,7 @@ func TestOptionValidation(t *testing.T) {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", 5)
req.SetOption("B", "test")
res = cmd.Call(req)
@ -53,32 +47,32 @@ func TestOptionValidation(t *testing.T) {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("foo", 5)
res = cmd.Call(req)
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption(EncShort, "json")
res = cmd.Call(req)
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", "100")
res = cmd.Call(req)
if res.Error() != nil {
t.Error("Should have passed")
}
req = NewEmptyRequest()
req = NewRequest(nil, nil, nil, nil, opts)
req.SetOption("b", ":)")
res = cmd.Call(req)
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,
}
res := cmdB.Call(NewRequest([]string{"a"}, nil, nil, nil, nil))
if res.Error() == nil {
path := []string{"a"}
_, err := cmdB.GetOptions(path)
if err == nil {
t.Error("Should have failed (option name collision)")
}
res = cmdC.Call(NewEmptyRequest())
if res.Error() == nil {
_, err = cmdC.GetOptions(nil)
if err == nil {
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) {
var userEncoding string
if enc, found := req.Option(cmds.EncShort); found {
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)
}
userEncoding, _ := req.Option(cmds.EncShort).String()
req.SetOption(cmds.EncShort, cmds.JSON)
query, inputStream, err := getQuery(req)
if err != nil {
@ -72,7 +57,6 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) {
if len(userEncoding) > 0 {
req.SetOption(cmds.EncShort, userEncoding)
req.SetOption(cmds.EncLong, userEncoding)
}
return res, nil

View File

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

View File

@ -1,6 +1,9 @@
package commands
import "reflect"
import (
"errors"
"reflect"
)
// Types of Command options
const (
@ -30,7 +33,7 @@ func NewOption(kind reflect.Kind, names ...string) Option {
}
desc := names[len(names)-1]
names = names[:len(names)-2]
names = names[:len(names)-1]
return Option{
Names: names,
@ -55,6 +58,73 @@ func StringOption(names ...string) Option {
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
const (
EncShort = "enc"

View File

@ -21,15 +21,15 @@ type Context struct {
// Request represents a call to a command from a consumer
type Request interface {
Path() []string
Option(name string) (interface{}, bool)
Options() map[string]interface{}
Option(name string) *OptionValue
Options() optMap
SetOption(name string, val interface{})
Arguments() []interface{} // TODO: make argument value type instead of using interface{}
Context() *Context
SetContext(Context)
Command() *Command
ConvertOptions(options map[string]Option) error
ConvertOptions() error
}
type request struct {
@ -47,31 +47,34 @@ func (r *request) Path() []string {
}
// 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]
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
// find the option with the specified name
option, found := r.optionDefs[name]
if found {
// try all the possible names, break if we find a value
for _, n := range option.Names {
val, found := r.options[n]
if found {
return val, found
}
if !found {
return nil
}
// try all the possible names, break if we find a value
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
func (r *request) Options() map[string]interface{} {
func (r *request) Options() optMap {
output := make(optMap)
for k, v := range r.options {
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{})
for k, v := range r.options {
opt, ok := options[k]
opt, ok := r.optionDefs[k]
if !ok {
continue
}
@ -203,5 +206,9 @@ func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command, op
if optDefs == nil {
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
}
enc, found := r.req.Option(EncShort)
encStr, ok := enc.(string)
if !found || !ok || encStr == "" {
fmt.Println(r.req, r.req.Option(EncShort))
if !r.req.Option(EncShort).Found() {
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
if r.req.Command() != nil && r.req.Command().Marshallers != nil {
marshaller = r.req.Command().Marshallers[encType]
}
if marshaller == nil {
var ok bool
marshaller, ok = marshallers[encType]
if !ok {
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) {
req := NewEmptyRequest()
cmd := &Command{}
opts, _ := cmd.GetOptions(nil)
req := NewRequest(nil, nil, nil, nil, opts)
res := NewResponse(req)
res.SetOutput(TestOutput{"beep", "boop", 1337})
// get command global options so we can set the encoding option
cmd := Command{}
options, err := cmd.GetOptions(nil)
if err != nil {
t.Error(err)
}
_, err = res.Marshal()
_, err := res.Marshal()
if err == nil {
t.Error("Should have failed (no encoding type specified in request)")
}
req.SetOption(EncShort, JSON)
req.ConvertOptions(options)
bytes, err := res.Marshal()
if err != nil {

View File

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

View File

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

View File

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

View File

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