rsapi/commands.go
func (a *API) ParseCommand(cmd, hrefPrefix string, values ActionCommands) (*ParsedCommand, error) {
	// 0. Show help if requested
	val, ok := values[cmd]
	if !ok {
		return nil, fmt.Errorf("Unknown command '%s'", cmd)
	}
	if val.ShowHelp != "" {
		a.ShowHelp(cmd, hrefPrefix, values)
		return nil, nil
	}

	// 1. Initialize / find href as well as resource and action command definitions
	target, params, err := a.ParseCommandAndFlags(cmd, hrefPrefix, values)
	if err != nil {
		return nil, err
	}
	resource, action, path := target.Resource, target.Action, target.Path

	// 2. Coerce and validate given flag values, use array for payload as order is meaningful
	//    when building the data structure (e.g. in the case of array of hashes)
	var queryParams, payloadParams []APIParams
	var seen []string
	for _, p := range params {
		param, value, err := a.findParamAndValue(action, p)
		if err != nil {
			return nil, err
		}
		if param == nil {
			name := strings.SplitN(p, "=", 2)[0]
			if len(action.CommandFlags) > 0 {
				supported := make([]string, len(action.CommandFlags))
				for i, p := range action.CommandFlags {
					supported[i] = p.Name
				}
				return nil, fmt.Errorf("Unknown %s.%s flag '%s'. Supported flags are: %s",
					resource.Name, action.Name, name, strings.Join(supported, ", "))
			}
			return nil, fmt.Errorf("Unknown %s.%s flag '%s'. %s.%s does not accept any flag",
				resource.Name, action.Name, name, resource.Name, action.Name)
		}
		name := param.Name
		seen = append(seen, name)
		if err := validateFlagValue(value, param); err != nil {
			return nil, err
		}
		coerced := &queryParams
		if param.Location == metadata.PayloadParam {
			coerced = &payloadParams
		}
		switch param.Type {
		case "string", "[]string", "interface{}":
			*coerced = append(*coerced, APIParams{name: value})
		case "int", "[]int":
			val, err := strconv.Atoi(value)
			if err != nil {
				return nil, fmt.Errorf("Value for '%s' must be an integer, value provided was '%s'",
					name, value)
			}
			*coerced = append(*coerced, APIParams{name: val})
		case "bool", "[]bool":
			val, err := strconv.ParseBool(value)
			if err != nil {
				return nil, fmt.Errorf("Value for '%s' must be a bool, value provided was '%s'",
					name, value)
			}
			*coerced = append(*coerced, APIParams{name: val})
		case "*time.Time":
			// Try to parse the input as int64 and if it was successful, treat is as a Unix
			// time otherwise parse as RFC3339.
			if val, err := strconv.ParseInt(value, 10, 64); err == nil {
				*coerced = append(*coerced, APIParams{name: time.Unix(val, 0)})
			} else {
				val, err := time.Parse(time.RFC3339, value)
				if err != nil {
					return nil, fmt.Errorf("Invalid time '%s' for %s: %s", value, name, err)
				}
				*coerced = append(*coerced, APIParams{name: val})
			}
		case "map":
			velems := strings.SplitN(value, "=", 2)
			if len(velems) != 2 {
				return nil, fmt.Errorf("Value for '%s' must be of the form NAME=VALUE, got %s", name, value)
			}
			*coerced = append(*coerced, APIParams{fmt.Sprintf("%s[%s]", name, velems[0]): velems[1]})
		case "sourcefile":
			file, err := os.Open(value)
			if err != nil {
				return nil, fmt.Errorf("Invalid file upload path '%s' for %s: %s", value, name, err)
			}
			*coerced = append(*coerced, APIParams{name: &SourceUpload{Reader: file}})
		case "file":
			file, err := os.Open(value)
			if err != nil {
				return nil, fmt.Errorf("Invalid file upload path '%s' for %s: %s", value, name, err)
			}
			*coerced = append(*coerced, APIParams{name: &FileUpload{Name: name, Filename: value, Reader: file}})
		}
	}
	for _, p := range action.CommandFlags {
		var ok bool
		for _, s := range seen {
			if s == p.Name {
				ok = true
				break
			}
		}
		if p.Mandatory && !ok {
			return nil, fmt.Errorf("Missing required flag '%s'", p.Name)
		}
	}

	// Reconstruct data structure from flat values
	qParams, err := buildQuery(queryParams)
	if err != nil {
		return nil, err
	}
	pParams, err := buildPayload(payloadParams)
	if err != nil {
		return nil, err
	}

	return &ParsedCommand{path.HTTPMethod, path.Path, qParams, pParams}, nil
}
func (a *API) ParseCommand(cmd, hrefPrefix string, values ActionCommands) (*ParsedCommand, error) {
	// 0. Show help if requested
	val, ok := values[cmd]
	if !ok {
		return nil, fmt.Errorf("Unknown command '%s'", cmd)
	}
	if val.ShowHelp != "" {
		a.ShowHelp(cmd, hrefPrefix, values)
		return nil, nil
	}

	// 1. Initialize / find href as well as resource and action command definitions
	target, params, err := a.ParseCommandAndFlags(cmd, hrefPrefix, values)
	if err != nil {
		return nil, err
	}
	resource, action, path := target.Resource, target.Action, target.Path

	// 2. Coerce and validate given flag values, use array for payload as order is meaningful
	//    when building the data structure (e.g. in the case of array of hashes)
	var queryParams, payloadParams []APIParams
	var seen []string
	for _, p := range params {
		param, value, err := a.findParamAndValue(action, p)
		if err != nil {
			return nil, err
		}
		if param == nil {
			name := strings.SplitN(p, "=", 2)[0]
			if len(action.CommandFlags) > 0 {
				supported := make([]string, len(action.CommandFlags))
				for i, p := range action.CommandFlags {
					supported[i] = p.Name
				}
				return nil, fmt.Errorf("Unknown %s.%s flag '%s'. Supported flags are: %s",
					resource.Name, action.Name, name, strings.Join(supported, ", "))
			}
			return nil, fmt.Errorf("Unknown %s.%s flag '%s'. %s.%s does not accept any flag",
				resource.Name, action.Name, name, resource.Name, action.Name)
		}
		name := param.Name
		seen = append(seen, name)
		if err := validateFlagValue(value, param); err != nil {
			return nil, err
		}
		coerced := &queryParams
		if param.Location == metadata.PayloadParam {
			coerced = &payloadParams
		}
		switch param.Type {
		case "string", "[]string", "interface{}":
			*coerced = append(*coerced, APIParams{name: value})
		case "int", "[]int":
			val, err := strconv.Atoi(value)
			if err != nil {
				return nil, fmt.Errorf("Value for '%s' must be an integer, value provided was '%s'",
					name, value)
			}
			*coerced = append(*coerced, APIParams{name: val})
		case "bool", "[]bool":
			val, err := strconv.ParseBool(value)
			if err != nil {
				return nil, fmt.Errorf("Value for '%s' must be a bool, value provided was '%s'",
					name, value)
			}
			*coerced = append(*coerced, APIParams{name: val})
		case "*time.Time":
			// Try to parse the input as int64 and if it was successful, treat is as a Unix
			// time otherwise parse as RFC3339.
			if val, err := strconv.ParseInt(value, 10, 64); err == nil {
				*coerced = append(*coerced, APIParams{name: time.Unix(val, 0)})
			} else {
				val, err := time.Parse(time.RFC3339, value)
				if err != nil {
					return nil, fmt.Errorf("Invalid time '%s' for %s: %s", value, name, err)
				}
				*coerced = append(*coerced, APIParams{name: val})
			}
		case "map":
			velems := strings.SplitN(value, "=", 2)
			if len(velems) != 2 {
				return nil, fmt.Errorf("Value for '%s' must be of the form NAME=VALUE, got %s", name, value)
			}
			*coerced = append(*coerced, APIParams{fmt.Sprintf("%s[%s]", name, velems[0]): velems[1]})
		case "sourcefile":
			file, err := os.Open(value)
			if err != nil {
				return nil, fmt.Errorf("Invalid file upload path '%s' for %s: %s", value, name, err)
			}
			*coerced = append(*coerced, APIParams{name: &SourceUpload{Reader: file}})
		case "file":
			file, err := os.Open(value)
			if err != nil {
				return nil, fmt.Errorf("Invalid file upload path '%s' for %s: %s", value, name, err)
			}
			*coerced = append(*coerced, APIParams{name: &FileUpload{Name: name, Filename: value, Reader: file}})
		}
	}
	for _, p := range action.CommandFlags {
		var ok bool
		for _, s := range seen {
			if s == p.Name {
				ok = true
				break
			}
		}
		if p.Mandatory && !ok {
			return nil, fmt.Errorf("Missing required flag '%s'", p.Name)
		}
	}

	// Reconstruct data structure from flat values
	qParams, err := buildQuery(queryParams)
	if err != nil {
		return nil, err
	}
	pParams, err := buildPayload(payloadParams)
	if err != nil {
		return nil, err
	}

	return &ParsedCommand{path.HTTPMethod, path.Path, qParams, pParams}, nil
}
// ParseCommand actually runs a command.