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  }