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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12 13
// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
14 15 16 17 18 19 20 21
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)
22
		opts, stringArgs, err := parseOptions(input)
23 24 25 26 27 28 29
		if err != nil {
			return nil, nil, err
		}

		length := len(path)
		if length > maxLength {
			maxLength = length
30 31 32 33 34 35

			args, err := parseArgs(stringArgs, cmd)
			if err != nil {
				return nil, nil, err
			}

36
			req = cmds.NewRequest(path, opts, args, cmd)
37 38
			root = r
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39
	}
40

41 42 43 44
	if maxLength == 0 {
		return nil, nil, errors.New("Not a valid subcommand")
	}

45
	return req, root, nil
46 47
}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
53 54 55 56
	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}
57

58 59
		sub := cmd.Subcommand(blob)
		if sub == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61
			break
		}
62
		cmd = sub
63

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
64 65
		i++
	}
66

67
	return input[:i], input[i:], cmd
68 69
}

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81
		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""
82

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83 84 85 86
			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}
87

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88 89 90 91 92
			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}
93

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98
			opts[name] = value
99

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100 101 102 103
		} else {
			args = append(args, blob)
		}
	}
104

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
	return opts, args, nil
106
}
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

// Note that the argument handling here is dumb, it does not do any error-checking.
// (Arguments are further processed when the request is passed to the command to run)
func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) {
	args := make([]interface{}, len(cmd.Arguments))

	for i, arg := range cmd.Arguments {
		// TODO: handle variadic args
		if i >= len(stringArgs) {
			break
		}

		if arg.Type == cmds.ArgString {
			args[i] = stringArgs[i]

		} else {
			in, err := os.Open(stringArgs[i])
			if err != nil {
				return nil, err
			}
			args[i] = in
		}
	}

	return args, nil
}