command_line.go
func ParseCommandLine(app *kingpin.Application) (*cmd.CommandLine, error) {
	// 1. Register all commands
	app.Command("setup", "create config file, defaults to $HOME/.rsc, use '--config' to override")
	app.Command("json", "apply jsonselect expression to STDIN")
	RegisterClientCommands(app)

	// 2. Parse flags
	cmdLine := cmd.CommandLine{}
	app.Flag("config", "path to rsc config file").Short('c').Default(path.Join(os.Getenv("HOME"), ".rsc")).StringVar(&cmdLine.ConfigPath)
	app.Flag("retry", "Number of retry attempts for non-successful API responses (500, 503, and timeouts only)").Short('R').Default("0").IntVar(&cmdLine.Retry)
	app.Flag("account", "RightScale account ID").Short('a').IntVar(&cmdLine.Account)
	app.Flag("host", "RightScale login endpoint (e.g. 'us-3.rightscale.com')").Short('h').StringVar(&cmdLine.Host)
	app.Flag("email", "Login email, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").StringVar(&cmdLine.Username)
	app.Flag("pwd", "Login password, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").StringVar(&cmdLine.Password)
	app.Flag("refreshToken", "OAuth refresh token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('r').StringVar(&cmdLine.OAuthToken)
	app.Flag("accessToken", "OAuth access token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('s').StringVar(&cmdLine.OAuthAccessToken)
	app.Flag("apiToken", "Instance API token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('p').StringVar(&cmdLine.APIToken)
	app.Flag("rl10", "Proxy requests through RightLink 10 agent, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").BoolVar(&cmdLine.RL10)
	app.Flag("noAuth", "Make unauthenticated requests, used for testing").BoolVar(&cmdLine.NoAuth)
	app.Flag("timeout", "Set the request timeout, defaults to 300s").Short('t').Default("300").IntVar(&cmdLine.Timeout)
	app.Flag("x1", "Extract single value using JSON:select").StringVar(&cmdLine.ExtractOneSelect)
	app.Flag("xm", "Extract zero, one or more values using JSON:select and return newline separated list").StringVar(&cmdLine.ExtractSelector)
	app.Flag("xj", "Extract zero, one or more values using JSON:select and return JSON").StringVar(&cmdLine.ExtractSelectorJSON)
	app.Flag("xh", "Extract header with given name").StringVar(&cmdLine.ExtractHeader)
	app.Flag("fetch", "Fetch resource with href present in 'Location' header").BoolVar(&cmdLine.FetchResource)
	app.Flag("dump", "Dump HTTP request and response. Possible values are 'debug' or 'json'.").EnumVar(&cmdLine.Dump, "debug", "json", "record")
	app.Flag("verbose", "Dump HTTP request and response including auth requests and headers, enables --dump=debug by default, use --dump=json to switch format").Short('v').BoolVar(&cmdLine.Verbose)
	app.Flag("pp", "Pretty print response body").BoolVar(&cmdLine.Pretty)

	// Keep around for a few releases for backwards compatibility
	app.Flag("key", "OAuth refresh token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('k').Hidden().StringVar(&cmdLine.OAuthToken)

	args := os.Args[1:]
	if len(args) == 0 {
		args = []string{"--help"}
	}
	// This is a bit hacky: basically doing `rsc api15 index clouds --help` results
	// in a command line that kingpin hijacks. So capture the `--help` try parsing
	// without it so we can print our own help.
	lastArgIndex := len(args)
	help := args[lastArgIndex-1]
	var cmd string
	var err error
	if help == "--help" || help == "-h" || help == "-help" || help == "-?" {
		cmdLine.ShowHelp = true
		lastArgIndex--
		cmd, err = app.Parse(args[:lastArgIndex])
	} else {
		cmd, err = app.Parse(args)
	}
	if err != nil {
		return nil, err
	}

	// 3. Complement with defaults from config at given path
	if !cmdLine.NoAuth {
		if config, err := LoadConfig(cmdLine.ConfigPath); err == nil {
			if cmdLine.OAuthAccessToken == "" && cmdLine.OAuthToken == "" {
				if cmdLine.Account == 0 {
					cmdLine.Account = config.Account
				}
				if cmdLine.Username == "" {
					cmdLine.Username = config.Email
				}
				if cmdLine.Password == "" {
					cmdLine.Password = config.Password
				}
			}
			if cmdLine.Host == "" {
				cmdLine.Host = config.LoginHost
			}
			if cmdLine.OAuthToken == "" {
				cmdLine.OAuthToken = config.RefreshToken
			}
		}
	}
	cmdLine.Command = cmd

	// 4. Special RL10 case (auth is handled differently)
	if strings.Split(cmdLine.Command, " ")[0] == "rl10" {
		cmdLine.RL10 = true
	}

	// 6. Validate we have everything we need
	validateCommandLine(&cmdLine)

	// 7. We're done
	return &cmdLine, nil
}
func ParseCommandLine(app *kingpin.Application) (*cmd.CommandLine, error) {
	// 1. Register all commands
	app.Command("setup", "create config file, defaults to $HOME/.rsc, use '--config' to override")
	app.Command("json", "apply jsonselect expression to STDIN")
	RegisterClientCommands(app)

	// 2. Parse flags
	cmdLine := cmd.CommandLine{}
	app.Flag("config", "path to rsc config file").Short('c').Default(path.Join(os.Getenv("HOME"), ".rsc")).StringVar(&cmdLine.ConfigPath)
	app.Flag("retry", "Number of retry attempts for non-successful API responses (500, 503, and timeouts only)").Short('R').Default("0").IntVar(&cmdLine.Retry)
	app.Flag("account", "RightScale account ID").Short('a').IntVar(&cmdLine.Account)
	app.Flag("host", "RightScale login endpoint (e.g. 'us-3.rightscale.com')").Short('h').StringVar(&cmdLine.Host)
	app.Flag("email", "Login email, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").StringVar(&cmdLine.Username)
	app.Flag("pwd", "Login password, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").StringVar(&cmdLine.Password)
	app.Flag("refreshToken", "OAuth refresh token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('r').StringVar(&cmdLine.OAuthToken)
	app.Flag("accessToken", "OAuth access token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('s').StringVar(&cmdLine.OAuthAccessToken)
	app.Flag("apiToken", "Instance API token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('p').StringVar(&cmdLine.APIToken)
	app.Flag("rl10", "Proxy requests through RightLink 10 agent, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").BoolVar(&cmdLine.RL10)
	app.Flag("noAuth", "Make unauthenticated requests, used for testing").BoolVar(&cmdLine.NoAuth)
	app.Flag("timeout", "Set the request timeout, defaults to 300s").Short('t').Default("300").IntVar(&cmdLine.Timeout)
	app.Flag("x1", "Extract single value using JSON:select").StringVar(&cmdLine.ExtractOneSelect)
	app.Flag("xm", "Extract zero, one or more values using JSON:select and return newline separated list").StringVar(&cmdLine.ExtractSelector)
	app.Flag("xj", "Extract zero, one or more values using JSON:select and return JSON").StringVar(&cmdLine.ExtractSelectorJSON)
	app.Flag("xh", "Extract header with given name").StringVar(&cmdLine.ExtractHeader)
	app.Flag("fetch", "Fetch resource with href present in 'Location' header").BoolVar(&cmdLine.FetchResource)
	app.Flag("dump", "Dump HTTP request and response. Possible values are 'debug' or 'json'.").EnumVar(&cmdLine.Dump, "debug", "json", "record")
	app.Flag("verbose", "Dump HTTP request and response including auth requests and headers, enables --dump=debug by default, use --dump=json to switch format").Short('v').BoolVar(&cmdLine.Verbose)
	app.Flag("pp", "Pretty print response body").BoolVar(&cmdLine.Pretty)

	// Keep around for a few releases for backwards compatibility
	app.Flag("key", "OAuth refresh token, use --email and --pwd or use --refreshToken, --accessToken, --apiToken or --rl10").Short('k').Hidden().StringVar(&cmdLine.OAuthToken)

	args := os.Args[1:]
	if len(args) == 0 {
		args = []string{"--help"}
	}
	// This is a bit hacky: basically doing `rsc api15 index clouds --help` results
	// in a command line that kingpin hijacks. So capture the `--help` try parsing
	// without it so we can print our own help.
	lastArgIndex := len(args)
	help := args[lastArgIndex-1]
	var cmd string
	var err error
	if help == "--help" || help == "-h" || help == "-help" || help == "-?" {
		cmdLine.ShowHelp = true
		lastArgIndex--
		cmd, err = app.Parse(args[:lastArgIndex])
	} else {
		cmd, err = app.Parse(args)
	}
	if err != nil {
		return nil, err
	}

	// 3. Complement with defaults from config at given path
	if !cmdLine.NoAuth {
		if config, err := LoadConfig(cmdLine.ConfigPath); err == nil {
			if cmdLine.OAuthAccessToken == "" && cmdLine.OAuthToken == "" {
				if cmdLine.Account == 0 {
					cmdLine.Account = config.Account
				}
				if cmdLine.Username == "" {
					cmdLine.Username = config.Email
				}
				if cmdLine.Password == "" {
					cmdLine.Password = config.Password
				}
			}
			if cmdLine.Host == "" {
				cmdLine.Host = config.LoginHost
			}
			if cmdLine.OAuthToken == "" {
				cmdLine.OAuthToken = config.RefreshToken
			}
		}
	}
	cmdLine.Command = cmd

	// 4. Special RL10 case (auth is handled differently)
	if strings.Split(cmdLine.Command, " ")[0] == "rl10" {
		cmdLine.RL10 = true
	}

	// 6. Validate we have everything we need
	validateCommandLine(&cmdLine)

	// 7. We're done
	return &cmdLine, nil
}
// ParseCommandLine retrieves the command and top level flag values.