github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/commands/cli/parse.go (about) 1 package cli 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "strings" 8 9 cmds "github.com/jbenet/go-ipfs/commands" 10 ) 11 12 // Parse parses the input commandline string (cmd, flags, and args). 13 // returns the corresponding command Request object. 14 func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { 15 var root, cmd *cmds.Command 16 var path, stringArgs []string 17 var opts map[string]interface{} 18 19 // use the root that matches the longest path (most accurately matches request) 20 maxLength := 0 21 for _, r := range roots { 22 p, i, c := parsePath(input, r) 23 o, s, err := parseOptions(i) 24 if err != nil { 25 return nil, root, c, p, err 26 } 27 28 length := len(p) 29 if length > maxLength { 30 maxLength = length 31 root = r 32 path = p 33 cmd = c 34 opts = o 35 stringArgs = s 36 } 37 } 38 39 if maxLength == 0 { 40 return nil, root, nil, path, errors.New("Not a valid subcommand") 41 } 42 43 args, err := parseArgs(stringArgs, cmd) 44 if err != nil { 45 return nil, root, cmd, path, err 46 } 47 48 optDefs, err := root.GetOptions(path) 49 if err != nil { 50 return nil, root, cmd, path, err 51 } 52 53 req := cmds.NewRequest(path, opts, args, cmd, optDefs) 54 55 err = cmd.CheckArguments(req) 56 if err != nil { 57 return req, root, cmd, path, err 58 } 59 60 return req, root, cmd, path, nil 61 } 62 63 // parsePath separates the command path and the opts and args from a command string 64 // returns command path slice, rest slice, and the corresponding *cmd.Command 65 func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) { 66 cmd := root 67 i := 0 68 69 for _, blob := range input { 70 if strings.HasPrefix(blob, "-") { 71 break 72 } 73 74 sub := cmd.Subcommand(blob) 75 if sub == nil { 76 break 77 } 78 cmd = sub 79 80 i++ 81 } 82 83 return input[:i], input[i:], cmd 84 } 85 86 // parseOptions parses the raw string values of the given options 87 // returns the parsed options as strings, along with the CLI args 88 func parseOptions(input []string) (map[string]interface{}, []string, error) { 89 opts := make(map[string]interface{}) 90 args := []string{} 91 92 for i := 0; i < len(input); i++ { 93 blob := input[i] 94 95 if strings.HasPrefix(blob, "-") { 96 name := blob[1:] 97 value := "" 98 99 // support single and double dash 100 if strings.HasPrefix(name, "-") { 101 name = name[1:] 102 } 103 104 if strings.Contains(name, "=") { 105 split := strings.SplitN(name, "=", 2) 106 name = split[0] 107 value = split[1] 108 } 109 110 if _, ok := opts[name]; ok { 111 return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name) 112 } 113 114 opts[name] = value 115 116 } else { 117 args = append(args, blob) 118 } 119 } 120 121 return opts, args, nil 122 } 123 124 func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { 125 args := make([]interface{}, 0) 126 127 // count required argument definitions 128 lenRequired := 0 129 for _, argDef := range cmd.Arguments { 130 if argDef.Required { 131 lenRequired++ 132 } 133 } 134 135 j := 0 136 for _, argDef := range cmd.Arguments { 137 // skip optional argument definitions if there aren't sufficient remaining values 138 if len(stringArgs)-j <= lenRequired && !argDef.Required { 139 continue 140 } else if argDef.Required { 141 lenRequired-- 142 } 143 144 if j >= len(stringArgs) { 145 break 146 } 147 148 if argDef.Variadic { 149 for _, arg := range stringArgs[j:] { 150 var err error 151 args, err = appendArg(args, argDef, arg) 152 if err != nil { 153 return nil, err 154 } 155 j++ 156 } 157 } else { 158 var err error 159 args, err = appendArg(args, argDef, stringArgs[j]) 160 if err != nil { 161 return nil, err 162 } 163 j++ 164 } 165 } 166 167 if len(stringArgs)-j > 0 { 168 args = append(args, make([]interface{}, len(stringArgs)-j)) 169 } 170 171 return args, nil 172 } 173 174 func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interface{}, error) { 175 if argDef.Type == cmds.ArgString { 176 return append(args, value), nil 177 178 } else { 179 in, err := os.Open(value) 180 if err != nil { 181 return nil, err 182 } 183 return append(args, in), nil 184 } 185 }