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

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11
// Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
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
31
	}
32

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

36
	return req, root, nil
37 38
}

39
// parsePath gets the command path from the command line input
40
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42
	cmd := root
	i := 0
43

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44 45 46 47
	for _, blob := range input {
		if strings.HasPrefix(blob, "-") {
			break
		}
48

49 50
		sub := cmd.Subcommand(blob)
		if sub == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51 52
			break
		}
53
		cmd = sub
54

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
55 56
		i++
	}
57

58
	return input[:i], input[i:], cmd
59 60
}

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

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
70 71 72
		if strings.HasPrefix(blob, "-") {
			name := blob[1:]
			value := ""
73

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
74 75 76 77
			// support single and double dash
			if strings.HasPrefix(name, "-") {
				name = name[1:]
			}
78

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81 82 83
			if strings.Contains(name, "=") {
				split := strings.SplitN(name, "=", 2)
				name = split[0]
				value = split[1]
			}
84

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
89
			opts[name] = value
90

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91 92 93 94
		} else {
			args = append(args, blob)
		}
	}
95

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96
	return opts, args, nil
97
}