mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-09-19 18:47:26 +03:00
188 lines
4.9 KiB
Go
188 lines
4.9 KiB
Go
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
|
|
|
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
var _ = fmt.Print
|
|
|
|
type OptionType int
|
|
|
|
const (
|
|
StringOption OptionType = iota
|
|
IntegerOption
|
|
FloatOption
|
|
BoolOption
|
|
CountOption
|
|
)
|
|
|
|
type Alias struct {
|
|
NameWithoutHyphens string
|
|
IsShort bool
|
|
IsUnset bool
|
|
}
|
|
|
|
func (self *Alias) String() string {
|
|
if self.IsShort {
|
|
return "-" + self.NameWithoutHyphens
|
|
}
|
|
return "--" + self.NameWithoutHyphens
|
|
}
|
|
|
|
type OptionSpec struct {
|
|
Name string
|
|
Type string
|
|
Dest string
|
|
Choices string
|
|
Depth int
|
|
Default string
|
|
Help string
|
|
Completer CompletionFunc
|
|
}
|
|
|
|
type Option struct {
|
|
Name string
|
|
Aliases []Alias
|
|
Choices []string
|
|
Default string
|
|
OptionType OptionType
|
|
Hidden bool
|
|
Depth int
|
|
Help string
|
|
IsList bool
|
|
Parent *Command
|
|
Completer CompletionFunc
|
|
|
|
values_from_cmdline []string
|
|
parsed_values_from_cmdline []any
|
|
parsed_default any
|
|
seen_option string
|
|
}
|
|
|
|
func (self *Option) reset() {
|
|
self.values_from_cmdline = self.values_from_cmdline[:0]
|
|
self.parsed_values_from_cmdline = self.parsed_values_from_cmdline[:0]
|
|
self.seen_option = ""
|
|
}
|
|
|
|
func (self *Option) needs_argument() bool {
|
|
return self.OptionType != BoolOption && self.OptionType != CountOption
|
|
}
|
|
|
|
func (self *Option) HasAlias(name_without_hyphens string, is_short bool) bool {
|
|
for _, a := range self.Aliases {
|
|
if a.IsShort == is_short && a.NameWithoutHyphens == name_without_hyphens {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type ParseError struct {
|
|
Option *Option
|
|
Message string
|
|
}
|
|
|
|
func (self *ParseError) Error() string { return self.Message }
|
|
|
|
func NormalizeOptionName(name string) string {
|
|
return strings.ReplaceAll(strings.TrimLeft(name, "-"), "_", "-")
|
|
}
|
|
|
|
func (self *Option) parsed_value() any {
|
|
if len(self.values_from_cmdline) == 0 {
|
|
return self.parsed_default
|
|
}
|
|
switch self.OptionType {
|
|
case CountOption:
|
|
return len(self.parsed_values_from_cmdline)
|
|
case StringOption:
|
|
if self.IsList {
|
|
ans := make([]string, len(self.parsed_values_from_cmdline))
|
|
for i, x := range self.parsed_values_from_cmdline {
|
|
ans[i] = x.(string)
|
|
}
|
|
return ans
|
|
}
|
|
fallthrough
|
|
default:
|
|
return self.parsed_values_from_cmdline[len(self.parsed_values_from_cmdline)-1]
|
|
}
|
|
}
|
|
|
|
func (self *Option) parse_value(val string) (any, error) {
|
|
switch self.OptionType {
|
|
case BoolOption:
|
|
switch val {
|
|
case "true":
|
|
return true, nil
|
|
case "false":
|
|
return false, nil
|
|
default:
|
|
return nil, &ParseError{Option: self, Message: fmt.Sprintf(":yellow:`%s` is not a valid value for :bold:`%s`.", val, self.seen_option)}
|
|
}
|
|
case StringOption:
|
|
return val, nil
|
|
case IntegerOption, CountOption:
|
|
pval, err := strconv.ParseInt(val, 0, 0)
|
|
if err != nil {
|
|
return nil, &ParseError{Option: self, Message: fmt.Sprintf(
|
|
":yellow:`%s` is not a valid number for :bold:`%s`. Only integers in decimal, hexadecimal, binary or octal notation are accepted.", val, self.seen_option)}
|
|
}
|
|
return int(pval), nil
|
|
case FloatOption:
|
|
pval, err := strconv.ParseFloat(val, 64)
|
|
if err != nil {
|
|
return nil, &ParseError{Option: self, Message: fmt.Sprintf(
|
|
":yellow:`%s` is not a valid number for :bold:`%s`. Only floats in decimal and hexadecimal notation are accepted.", val, self.seen_option)}
|
|
}
|
|
return pval, nil
|
|
default:
|
|
return nil, &ParseError{Option: self, Message: fmt.Sprintf("Unknown option type for %s", self.Name)}
|
|
}
|
|
}
|
|
|
|
func (self *Option) add_value(val string) error {
|
|
name_without_hyphens := NormalizeOptionName(self.seen_option)
|
|
switch self.OptionType {
|
|
case BoolOption:
|
|
for _, x := range self.Aliases {
|
|
if x.NameWithoutHyphens == name_without_hyphens {
|
|
if x.IsUnset {
|
|
self.values_from_cmdline = append(self.values_from_cmdline, "false")
|
|
self.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, false)
|
|
} else {
|
|
self.values_from_cmdline = append(self.values_from_cmdline, "true")
|
|
self.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, true)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
case StringOption:
|
|
if self.Choices != nil && !slices.Contains(self.Choices, val) {
|
|
return &ParseError{Option: self, Message: fmt.Sprintf(":yellow:`%s` is not a valid value for :bold:`%s`. Valid values: %s",
|
|
val, self.seen_option, strings.Join(self.Choices, ", "),
|
|
)}
|
|
}
|
|
self.values_from_cmdline = append(self.values_from_cmdline, val)
|
|
self.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, val)
|
|
case IntegerOption, FloatOption:
|
|
pval, err := self.parse_value(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
self.values_from_cmdline = append(self.values_from_cmdline, val)
|
|
self.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, pval)
|
|
case CountOption:
|
|
self.values_from_cmdline = append(self.values_from_cmdline, val)
|
|
self.parsed_values_from_cmdline = append(self.parsed_values_from_cmdline, 1)
|
|
}
|
|
return nil
|
|
}
|