Commit 7673ce6f authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

fmt, lint, + vet commands/

parent a9fa767b
package cli
import (
"fmt"
"strings"
"fmt"
"strings"
"github.com/jbenet/go-ipfs/commands"
"github.com/jbenet/go-ipfs/commands"
)
// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
func Parse(input []string, root *commands.Command) (*commands.Request, error) {
path, input, err := parsePath(input, root)
if err != nil {
return nil, err
}
path, input, err := parsePath(input, root)
if err != nil {
return nil, err
}
opts, args, err := parseOptions(input)
if err != nil {
return nil, err
}
opts, args, err := parseOptions(input)
if err != nil {
return nil, err
}
return commands.NewRequest(path, opts, args), nil
return commands.NewRequest(path, opts, args), nil
}
// parsePath gets the command path from the command line input
func parsePath(input []string, root *commands.Command) ([]string, []string, error) {
cmd := root
i := 0
cmd := root
i := 0
for _, blob := range input {
if strings.HasPrefix(blob, "-") {
break
}
for _, blob := range input {
if strings.HasPrefix(blob, "-") {
break
}
cmd := cmd.Sub(blob)
if cmd == nil {
break
}
cmd := cmd.Sub(blob)
if cmd == nil {
break
}
i++
}
i++
}
return input[:i], input[i:], nil
return input[:i], input[i:], nil
}
// parseOptions parses the raw string values of the given options
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
opts := make(map[string]interface{})
args := make([]string, 0)
opts := make(map[string]interface{})
args := []string{}
for i := 0; i < len(input); i++ {
blob := input[i]
for i := 0; i < len(input); i++ {
blob := input[i]
if strings.HasPrefix(blob, "-") {
name := blob[1:]
value := ""
if strings.HasPrefix(blob, "-") {
name := blob[1:]
value := ""
// support single and double dash
if strings.HasPrefix(name, "-") {
name = name[1:]
}
// support single and double dash
if strings.HasPrefix(name, "-") {
name = name[1:]
}
if strings.Contains(name, "=") {
split := strings.SplitN(name, "=", 2)
name = split[0]
value = split[1]
}
if strings.Contains(name, "=") {
split := strings.SplitN(name, "=", 2)
name = split[0]
value = split[1]
}
if _, ok := opts[name]; ok {
return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
}
if _, ok := opts[name]; ok {
return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
}
opts[name] = value
opts[name] = value
} else {
args = append(args, blob)
}
}
} else {
args = append(args, blob)
}
}
return opts, args, nil
return opts, args, nil
}
package cli
import (
//"fmt"
"testing"
//"fmt"
"testing"
"github.com/jbenet/go-ipfs/commands"
"github.com/jbenet/go-ipfs/commands"
)
func TestOptionParsing(t *testing.T) {
cmd := &commands.Command{
Options: []commands.Option{
commands.Option{ []string{"b"}, commands.String },
},
}
cmd.Register("test", &commands.Command{})
cmd := &commands.Command{
Options: []commands.Option{
commands.Option{Names: []string{"b"}, Type: commands.String},
},
}
cmd.Register("test", &commands.Command{})
opts, input, err := parseOptions([]string{ "--beep", "-boop=lol", "test2", "-c", "beep", "--foo=5" })
/*for k, v := range opts {
fmt.Printf("%s: %s\n", k, v)
}
fmt.Printf("%s\n", input)*/
if err != nil {
t.Error("Should have passed")
}
if len(opts) != 4 || opts["beep"] != "" || opts["boop"] != "lol" || opts["c"] != "" || opts["foo"] != "5" {
t.Error("Returned options were defferent than expected: %v", opts)
}
if len(input) != 2 || input[0] != "test2" || input[1] != "beep" {
t.Error("Returned input was different than expected: %v", input)
}
opts, input, err := parseOptions([]string{"--beep", "-boop=lol", "test2", "-c", "beep", "--foo=5"})
/*for k, v := range opts {
fmt.Printf("%s: %s\n", k, v)
}
fmt.Printf("%s\n", input)*/
if err != nil {
t.Error("Should have passed")
}
if len(opts) != 4 || opts["beep"] != "" || opts["boop"] != "lol" || opts["c"] != "" || opts["foo"] != "5" {
t.Error("Returned options were defferent than expected: %v", opts)
}
if len(input) != 2 || input[0] != "test2" || input[1] != "beep" {
t.Error("Returned input was different than expected: %v", input)
}
_, _, err = parseOptions([]string{ "-beep=1", "-boop=2", "-beep=3" })
if err == nil {
t.Error("Should have failed (duplicate option name)")
}
_, _, err = parseOptions([]string{"-beep=1", "-boop=2", "-beep=3"})
if err == nil {
t.Error("Should have failed (duplicate option name)")
}
path, args, err := parsePath([]string{ "test", "beep", "boop" }, cmd)
if err != nil {
t.Error("Should have passed")
}
if len(path) != 1 || path[0] != "test" {
t.Error("Returned path was defferent than expected: %v", path)
}
if len(args) != 2 || args[0] != "beep" || args[1] != "boop" {
t.Error("Returned args were different than expected: %v", args)
}
path, args, err := parsePath([]string{"test", "beep", "boop"}, cmd)
if err != nil {
t.Error("Should have passed")
}
if len(path) != 1 || path[0] != "test" {
t.Error("Returned path was defferent than expected: %v", path)
}
if len(args) != 2 || args[0] != "beep" || args[1] != "boop" {
t.Error("Returned args were different than expected: %v", args)
}
}
......@@ -6,6 +6,7 @@ import (
"strings"
)
// Command is an object that defines a command.
type Command struct {
Help string
Options []Option
......@@ -13,7 +14,8 @@ type Command struct {
subcommands map[string]*Command
}
var NotCallableError = errors.New("This command can't be called directly. Try one of its subcommands.")
// ErrNotCallable signals a command that cannot be called.
var ErrNotCallable = errors.New("This command can't be called directly. Try one of its subcommands.")
// Register adds a subcommand
func (c *Command) Register(id string, sub *Command) error {
......@@ -44,25 +46,25 @@ func (c *Command) Call(req *Request) *Response {
cmds, err := c.Resolve(req.path)
if err != nil {
res.SetError(err, Client)
res.SetError(err, ErrClient)
return res
}
cmd := cmds[len(cmds)-1]
if cmd.f == nil {
res.SetError(NotCallableError, Client)
res.SetError(ErrNotCallable, ErrClient)
return res
}
options, err := c.GetOptions(req.path)
if err != nil {
res.SetError(err, Client)
res.SetError(err, ErrClient)
return res
}
err = req.convertOptions(options)
if err != nil {
res.SetError(err, Client)
res.SetError(err, ErrClient)
return res
}
......@@ -91,6 +93,7 @@ func (c *Command) Resolve(path []string) ([]*Command, error) {
return cmds, nil
}
// Get resolves and returns the Command addressed by path
func (c *Command) Get(path []string) (*Command, error) {
cmds, err := c.Resolve(path)
if err != nil {
......
......@@ -2,6 +2,7 @@ package commands
import "reflect"
// Types of Command options
const (
Invalid = reflect.Invalid
Bool = reflect.Bool
......@@ -22,11 +23,11 @@ type Option struct {
}
// options that are used by this package
var globalOptions []Option = []Option{
var globalOptions = []Option{
Option{[]string{"enc", "encoding"}, String},
}
// the above array of Options, wrapped in a Command
var globalCommand *Command = &Command{
var globalCommand = &Command{
Options: globalOptions,
}
......@@ -7,11 +7,13 @@ import (
"strings"
)
// ErrorType signfies a category of errors
type ErrorType uint
// ErrorTypes convey what category of error ocurred
const (
Normal ErrorType = iota // general errors
Client // error was caused by the client, (e.g. invalid CLI usage)
ErrNormal ErrorType = iota // general errors
ErrClient // error was caused by the client, (e.g. invalid CLI usage)
// TODO: add more types of errors for better error-specific handling
)
......@@ -21,37 +23,44 @@ type Error struct {
Code ErrorType
}
func (e *Error) Error() string {
return fmt.Sprintf("%d error: %s", e.Code, e.Message)
}
// EncodingType defines a supported encoding
type EncodingType string
// Supported EncodingType constants.
const (
Json = "json"
Xml = "xml"
JSON = "json"
XML = "xml"
// TODO: support more encoding types
)
// Marshaller is a function used by coding types.
// TODO this should just be a `coding.Codec`
type Marshaller func(v interface{}) ([]byte, error)
var marshallers = map[EncodingType]Marshaller{
Json: json.Marshal,
Xml: xml.Marshal,
JSON: json.Marshal,
XML: xml.Marshal,
}
// Response is the result of a command request. Handlers write to the response,
// setting Error or Value. Response is returned to the client.
type Response struct {
req *Request
Error error
ErrorType ErrorType
Value interface{}
}
func (r *Response) SetError(err error, errType ErrorType) {
r.Error = err
r.ErrorType = errType
req *Request
Error *Error
Value interface{}
}
func (r *Response) FormatError() Error {
return Error{r.Error.Error(), r.ErrorType}
// SetError updates the response Error.
func (r *Response) SetError(err error, code ErrorType) {
r.Error = &Error{Message: err.Error(), Code: code}
}
// Marshal marshals out the response into a buffer. It uses the EncodingType
// on the Request to chose a Marshaller (Codec).
func (r *Response) Marshal() ([]byte, error) {
if r.Error == nil && r.Value == nil {
return nil, fmt.Errorf("No error or value set, there is nothing to marshal")
......@@ -69,9 +78,7 @@ func (r *Response) Marshal() ([]byte, error) {
}
if r.Error != nil {
err := r.FormatError()
return marshaller(err)
} else {
return marshaller(r.Value)
return marshaller(r.Error)
}
return marshaller(r.Value)
}
......@@ -30,7 +30,7 @@ func TestMarshalling(t *testing.T) {
t.Error("Should have failed (no encoding type specified in request)")
}
req.SetOption("enc", Json)
req.SetOption("enc", JSON)
req.convertOptions(options)
bytes, err := res.Marshal()
......@@ -42,7 +42,7 @@ func TestMarshalling(t *testing.T) {
t.Error("Incorrect JSON output")
}
res.SetError(fmt.Errorf("You broke something!"), Client)
res.SetError(fmt.Errorf("You broke something!"), ErrClient)
bytes, err = res.Marshal()
if err != nil {
t.Error("Should have passed")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment