Allow defining options using a struct
This commit is contained in:
parent
ef49634353
commit
8221713995
@ -99,51 +99,52 @@ type multi_scan struct {
|
|||||||
|
|
||||||
var mpat *regexp.Regexp
|
var mpat *regexp.Regexp
|
||||||
|
|
||||||
func option_from_string(overrides map[string]string, entries ...string) (*Option, error) {
|
func option_from_spec(spec OptionSpec) (*Option, error) {
|
||||||
if mpat == nil {
|
|
||||||
mpat = regexp.MustCompile("^([a-z]+)=(.+)")
|
|
||||||
}
|
|
||||||
ans := Option{
|
ans := Option{
|
||||||
|
Help: spec.Help,
|
||||||
values_from_cmdline: make([]string, 0, 1),
|
values_from_cmdline: make([]string, 0, 1),
|
||||||
parsed_values_from_cmdline: make([]any, 0, 1),
|
parsed_values_from_cmdline: make([]any, 0, 1),
|
||||||
}
|
}
|
||||||
scanner := utils.NewScanLines(entries...)
|
parts := strings.Split(spec.Name, " ")
|
||||||
in_help := false
|
ans.Name = camel_case_dest(parts[0])
|
||||||
prev_indent := 0
|
ans.Aliases = make([]Alias, 0, len(parts))
|
||||||
help := strings.Builder{}
|
for i, x := range parts {
|
||||||
help.Grow(2048)
|
ans.Aliases[i] = Alias{NameWithoutHyphens: strings.TrimLeft(x, "-"), IsShort: !strings.HasPrefix(x, "--")}
|
||||||
default_was_set := false
|
|
||||||
|
|
||||||
indent_of_line := func(x string) int {
|
|
||||||
return len(x) - len(strings.TrimLeft(x, " \n\t\v\f"))
|
|
||||||
}
|
}
|
||||||
|
if spec.Dest != "" {
|
||||||
set_default := func(x string) {
|
ans.Name = spec.Dest
|
||||||
if !default_was_set {
|
}
|
||||||
ans.Default = x
|
ans.Depth = spec.Depth
|
||||||
default_was_set = true
|
if spec.Choices != "" {
|
||||||
|
parts := strings.Split(spec.Choices, ",")
|
||||||
|
ans.Choices = make(map[string]bool, len(parts))
|
||||||
|
ans.OptionType = StringOption
|
||||||
|
for i, x := range parts {
|
||||||
|
x = strings.TrimSpace(x)
|
||||||
|
ans.Choices[x] = true
|
||||||
|
if i == 0 && ans.Default == "" {
|
||||||
|
ans.Default = x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
switch spec.Type {
|
||||||
set_type := func(v string) error {
|
|
||||||
switch v {
|
|
||||||
case "choice", "choices":
|
case "choice", "choices":
|
||||||
ans.OptionType = StringOption
|
ans.OptionType = StringOption
|
||||||
case "int":
|
case "int":
|
||||||
ans.OptionType = IntegerOption
|
ans.OptionType = IntegerOption
|
||||||
set_default("0")
|
ans.Default = "0"
|
||||||
case "float":
|
case "float":
|
||||||
ans.OptionType = FloatOption
|
ans.OptionType = FloatOption
|
||||||
set_default("0")
|
ans.Default = "0"
|
||||||
case "count":
|
case "count":
|
||||||
ans.OptionType = CountOption
|
ans.OptionType = CountOption
|
||||||
set_default("0")
|
ans.Default = "0"
|
||||||
case "bool-set":
|
case "bool-set":
|
||||||
ans.OptionType = BoolOption
|
ans.OptionType = BoolOption
|
||||||
set_default("false")
|
ans.Default = "false"
|
||||||
case "bool-reset":
|
case "bool-reset":
|
||||||
ans.OptionType = BoolOption
|
ans.OptionType = BoolOption
|
||||||
set_default("true")
|
ans.Default = "true"
|
||||||
for _, a := range ans.Aliases {
|
for _, a := range ans.Aliases {
|
||||||
a.IsUnset = true
|
a.IsUnset = true
|
||||||
}
|
}
|
||||||
@ -153,99 +154,14 @@ func option_from_string(overrides map[string]string, entries ...string) (*Option
|
|||||||
case "str", "string":
|
case "str", "string":
|
||||||
ans.OptionType = StringOption
|
ans.OptionType = StringOption
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown option type: %s", v)
|
return nil, fmt.Errorf("Unknown option type: %s", spec.Type)
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if dq, found := overrides["type"]; found {
|
|
||||||
err := set_type(dq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for scanner.Scan() {
|
if spec.Default != "" {
|
||||||
line := scanner.Text()
|
ans.Default = spec.Default
|
||||||
if ans.Aliases == nil {
|
|
||||||
if strings.HasPrefix(line, "--") {
|
|
||||||
parts := strings.Split(line, " ")
|
|
||||||
if dq, found := overrides["dest"]; found {
|
|
||||||
ans.Name = camel_case_dest(dq)
|
|
||||||
} else {
|
|
||||||
ans.Name = camel_case_dest(parts[0])
|
|
||||||
}
|
|
||||||
ans.Aliases = make([]Alias, 0, len(parts))
|
|
||||||
for i, x := range parts {
|
|
||||||
ans.Aliases[i] = Alias{NameWithoutHyphens: strings.TrimLeft(x, "-"), IsShort: !strings.HasPrefix(x, "--")}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if in_help {
|
|
||||||
if line != "" {
|
|
||||||
current_indent := indent_of_line(line)
|
|
||||||
if current_indent > 1 {
|
|
||||||
if prev_indent == 0 {
|
|
||||||
help.WriteString("\n")
|
|
||||||
} else {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev_indent = current_indent
|
|
||||||
if !strings.HasSuffix(help.String(), "\n") {
|
|
||||||
help.WriteString(" ")
|
|
||||||
}
|
|
||||||
help.WriteString(line)
|
|
||||||
} else {
|
|
||||||
prev_indent = 0
|
|
||||||
help.WriteString("\n")
|
|
||||||
if !strings.HasSuffix(help.String(), "::") {
|
|
||||||
help.WriteString("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
matches := mpat.FindStringSubmatch(line)
|
|
||||||
if matches == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
k, v := matches[1], matches[2]
|
|
||||||
switch k {
|
|
||||||
case "choices":
|
|
||||||
parts := strings.Split(v, ",")
|
|
||||||
ans.Choices = make(map[string]bool, len(parts))
|
|
||||||
ans.OptionType = StringOption
|
|
||||||
for i, x := range parts {
|
|
||||||
x = strings.TrimSpace(x)
|
|
||||||
ans.Choices[x] = true
|
|
||||||
if i == 0 && ans.Default == "" {
|
|
||||||
ans.Default = x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "default":
|
|
||||||
ans.Default = v
|
|
||||||
case "dest":
|
|
||||||
if dq, found := overrides["dest"]; found {
|
|
||||||
ans.Name = camel_case_dest(dq)
|
|
||||||
} else {
|
|
||||||
ans.Name = camel_case_dest(v)
|
|
||||||
}
|
|
||||||
case "depth":
|
|
||||||
depth, err := strconv.ParseInt(v, 0, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ans.Depth = int(depth)
|
|
||||||
case "condition", "completion":
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Unknown option metadata key: %s", k)
|
|
||||||
case "type":
|
|
||||||
err := set_type(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ans.HelpText = help.String()
|
ans.Help = spec.Help
|
||||||
ans.Hidden = ans.HelpText == "!"
|
ans.Hidden = spec.Help == "!"
|
||||||
pval, err := ans.parse_value(ans.Default)
|
pval, err := ans.parse_value(ans.Default)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -262,3 +178,96 @@ func option_from_string(overrides map[string]string, entries ...string) (*Option
|
|||||||
}
|
}
|
||||||
return &ans, nil
|
return &ans, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func indent_of_line(x string) int {
|
||||||
|
return len(x) - len(strings.TrimLeft(x, " \n\t\v\f"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepare_help_text_for_display(raw string) string {
|
||||||
|
help := strings.Builder{}
|
||||||
|
help.Grow(len(raw) + 256)
|
||||||
|
prev_indent := 0
|
||||||
|
for _, line := range utils.Splitlines(raw) {
|
||||||
|
if line != "" {
|
||||||
|
current_indent := indent_of_line(line)
|
||||||
|
if current_indent > 1 {
|
||||||
|
if prev_indent == 0 {
|
||||||
|
help.WriteString("\n")
|
||||||
|
} else {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_indent = current_indent
|
||||||
|
if !strings.HasSuffix(help.String(), "\n") {
|
||||||
|
help.WriteString(" ")
|
||||||
|
}
|
||||||
|
help.WriteString(line)
|
||||||
|
} else {
|
||||||
|
prev_indent = 0
|
||||||
|
help.WriteString("\n")
|
||||||
|
if !strings.HasSuffix(help.String(), "::") {
|
||||||
|
help.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return help.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func option_from_string(overrides map[string]string, entries ...string) (*Option, error) {
|
||||||
|
if mpat == nil {
|
||||||
|
mpat = regexp.MustCompile("^([a-z]+)=(.+)")
|
||||||
|
}
|
||||||
|
spec := OptionSpec{}
|
||||||
|
scanner := utils.NewScanLines(entries...)
|
||||||
|
in_help := false
|
||||||
|
help := strings.Builder{}
|
||||||
|
help.Grow(2048)
|
||||||
|
|
||||||
|
if dq, found := overrides["type"]; found {
|
||||||
|
spec.Type = dq
|
||||||
|
}
|
||||||
|
if dq, found := overrides["dest"]; found {
|
||||||
|
spec.Dest = dq
|
||||||
|
}
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if spec.Name == "" {
|
||||||
|
if strings.HasPrefix(line, "--") {
|
||||||
|
spec.Name = line
|
||||||
|
}
|
||||||
|
} else if in_help {
|
||||||
|
spec.Help += line + "\n"
|
||||||
|
} else {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
matches := mpat.FindStringSubmatch(line)
|
||||||
|
if matches == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k, v := matches[1], matches[2]
|
||||||
|
switch k {
|
||||||
|
case "choices":
|
||||||
|
spec.Choices = v
|
||||||
|
case "default":
|
||||||
|
if overrides["default"] == "" {
|
||||||
|
spec.Default = v
|
||||||
|
}
|
||||||
|
case "dest":
|
||||||
|
if overrides["dest"] == "" {
|
||||||
|
spec.Dest = v
|
||||||
|
}
|
||||||
|
case "depth":
|
||||||
|
depth, err := strconv.ParseInt(v, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
spec.Depth = int(depth)
|
||||||
|
case "condition", "completion":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown option metadata key: %s", k)
|
||||||
|
case "type":
|
||||||
|
spec.Type = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return option_from_spec(spec)
|
||||||
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ type Option struct {
|
|||||||
OptionType OptionType
|
OptionType OptionType
|
||||||
Hidden bool
|
Hidden bool
|
||||||
Depth int
|
Depth int
|
||||||
HelpText string
|
Help string
|
||||||
IsList bool
|
IsList bool
|
||||||
Parent *Command
|
Parent *Command
|
||||||
|
|
||||||
@ -204,6 +204,16 @@ func (self *CommandGroup) FindSubCommand(name string) *Command {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OptionSpec struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Dest string
|
||||||
|
Choices string
|
||||||
|
Depth int
|
||||||
|
Default string
|
||||||
|
Help string
|
||||||
|
}
|
||||||
|
|
||||||
type OptionGroup struct { // {{{
|
type OptionGroup struct { // {{{
|
||||||
Options []*Option
|
Options []*Option
|
||||||
Title string
|
Title string
|
||||||
@ -219,7 +229,15 @@ func (self *OptionGroup) Clone(parent *Command) *OptionGroup {
|
|||||||
return &ans
|
return &ans
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *OptionGroup) AddOption(parent *Command, items ...string) (*Option, error) {
|
func (self *OptionGroup) AddOption(parent *Command, spec OptionSpec) (*Option, error) {
|
||||||
|
ans, err := option_from_spec(spec)
|
||||||
|
if err == nil {
|
||||||
|
ans.Parent = parent
|
||||||
|
}
|
||||||
|
return ans, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *OptionGroup) AddOptionFromString(parent *Command, items ...string) (*Option, error) {
|
||||||
ans, err := OptionFromString(items...)
|
ans, err := OptionFromString(items...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ans.Parent = parent
|
ans.Parent = parent
|
||||||
@ -338,12 +356,10 @@ func (self *Command) Validate() error {
|
|||||||
if seen_flags["-h"] || seen_flags["--help"] {
|
if seen_flags["-h"] || seen_flags["--help"] {
|
||||||
return &ParseError{Message: fmt.Sprintf("The --help or -h flags are assigned to an option other than Help in %s", self.Name)}
|
return &ParseError{Message: fmt.Sprintf("The --help or -h flags are assigned to an option other than Help in %s", self.Name)}
|
||||||
}
|
}
|
||||||
self.AddOption(fmt.Sprintf(`
|
_, err := self.Add(OptionSpec{Name: "--help -h", Type: "bool-set", Help: "Show help for this command"})
|
||||||
--help -h
|
if err != nil {
|
||||||
type: bool-set
|
return err
|
||||||
Show help for this command
|
}
|
||||||
`))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -424,12 +440,20 @@ func (self *Command) AddOptionGroup(title string) *OptionGroup {
|
|||||||
return &ans
|
return &ans
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Command) AddOption(items ...string) (*Option, error) {
|
func (self *Command) AddOptionFromString(items ...string) (*Option, error) {
|
||||||
return self.AddOptionGroup("").AddOption(self, items...)
|
return self.AddOptionGroup("").AddOptionFromString(self, items...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Command) AddOptionToGroup(group string, items ...string) (*Option, error) {
|
func (self *Command) Add(s OptionSpec) (*Option, error) {
|
||||||
return self.AddOptionGroup(group).AddOption(self, items...)
|
return self.AddOptionGroup("").AddOption(self, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Command) AddOptionToGroupFromString(group string, items ...string) (*Option, error) {
|
||||||
|
return self.AddOptionGroup(group).AddOptionFromString(self, items...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Command) AddToGroup(group string, s OptionSpec) (*Option, error) {
|
||||||
|
return self.AddOptionGroup(group).AddOption(self, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Command) FindOption(name_with_hyphens string) *Option {
|
func (self *Command) FindOption(name_with_hyphens string) *Option {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user