parse.go 2.14 KB
Newer Older
1 2 3
package cli

import (
4
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5 6
	"fmt"
	"strings"
7

8
	cmds "github.com/jbenet/go-ipfs/commands"
9 10
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11 12
// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, error) {
	var req cmds.Request
	var root *cmds.Command

	// use the root that matches the longest path (most accurately matches request)
	maxLength := 0
	for _, r := range roots {
		path, input, cmd := parsePath(input, r)
		opts, args, err := parseOptions(input)
		if err != nil {
			return nil, nil, err
		}

		length := len(path)
		if length > maxLength {
			maxLength = length
			req = cmds.NewRequest(path, opts, args, nil, cmd)
			root = r
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32
	}
33

34 35 36 37
	if maxLength == 0 {
		return nil, nil, errors.New("Not a valid subcommand")
	}

38 39 40
	// TODO: figure out how to know when to read given file(s) as an input stream
	// (instead of filename arg string)

41
	return req, root, nil
42 43
}

44
// parsePath gets the command path from the command line input
45
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
46 47
	cmd := root
	i := 0
48

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49 50 51 52
	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}
53

54 55
		sub := cmd.Subcommand(blob)
		if sub == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56 57
			break
		}
58
		cmd = sub
59

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61
		i++
	}
62

63
	return input[:i], input[i:], cmd
64 65
}

66
// parseOptions parses the raw string values of the given options
67
// returns the parsed options as strings, along with the CLI args
68
func parseOptions(input []string) (map[string]interface{}, []interface{}, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69
	opts := make(map[string]interface{})
70
	args := []interface{}{}
71

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73
	for i := 0; i < len(input); i++ {
		blob := input[i]
74

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
75 76 77
		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""
78

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81 82
			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}
83

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84 85 86 87 88
			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}
89

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90 91 92
			if _, ok := opts[name]; ok {
				return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
			}
93

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
94
			opts[name] = value
95

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96 97 98 99
		} else {
			args = append(args, blob)
		}
	}
100

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101
	return opts, args, nil
102
}