112 lines
3.0 KiB
Go
112 lines
3.0 KiB
Go
package commandmeta
|
|
|
|
import "strings"
|
|
|
|
// FlagTokenKind identifies the portion of the shared option grammar a token uses.
|
|
type FlagTokenKind uint8
|
|
|
|
const (
|
|
OperandToken FlagTokenKind = iota
|
|
TerminatorToken
|
|
LongFlagToken
|
|
ShortFlagToken
|
|
)
|
|
|
|
// ClassifyFlagToken distinguishes option tokens without consuming their values.
|
|
func ClassifyFlagToken(token string) FlagTokenKind {
|
|
if token == "--" {
|
|
return TerminatorToken
|
|
}
|
|
if strings.HasPrefix(token, "--") && len(token) > 2 {
|
|
return LongFlagToken
|
|
}
|
|
if len(token) > 1 && token[0] == '-' {
|
|
return ShortFlagToken
|
|
}
|
|
return OperandToken
|
|
}
|
|
|
|
// SplitLongFlag separates the entered spelling from an optional attached value.
|
|
func SplitLongFlag(token string) (spelling, value string, attached bool) {
|
|
return strings.Cut(token, "=")
|
|
}
|
|
|
|
// FlagLookup maps canonical and applicable alias spellings to neutral flags.
|
|
type FlagLookup struct {
|
|
canonical map[string]Flag
|
|
aliases map[string]Flag
|
|
}
|
|
|
|
// NewFlagLookup includes the built-in help flag alongside the supplied flags.
|
|
func NewFlagLookup(flags []Flag, aliases []FlagAlias) FlagLookup {
|
|
canonical := make(map[string]Flag, len(flags)*2+2)
|
|
help := HelpFlag()
|
|
canonical["--help"], canonical["-h"] = help, help
|
|
for _, flag := range flags {
|
|
canonical["--"+flag.Name] = flag
|
|
if flag.Short != "" {
|
|
canonical["-"+flag.Short] = flag
|
|
}
|
|
}
|
|
lookup := FlagLookup{canonical: canonical, aliases: make(map[string]Flag, len(aliases))}
|
|
for _, alias := range aliases {
|
|
if target, ok := canonical[alias.Target]; ok {
|
|
lookup.aliases[alias.Source] = target
|
|
}
|
|
}
|
|
return lookup
|
|
}
|
|
|
|
// Find returns the canonical flag for an entered spelling.
|
|
func (lookup FlagLookup) Find(spelling string) (Flag, bool) {
|
|
flag, ok := lookup.canonical[spelling]
|
|
if !ok {
|
|
flag, ok = lookup.aliases[spelling]
|
|
}
|
|
return flag, ok
|
|
}
|
|
|
|
// FlagConsumption describes one flag within an option token. NextOffset is zero
|
|
// when the token is exhausted; otherwise it identifies the next short spelling.
|
|
type FlagConsumption struct {
|
|
Flag Flag
|
|
Spelling string
|
|
Found bool
|
|
Value string
|
|
Attached bool
|
|
NeedsNext bool
|
|
NextOffset int
|
|
}
|
|
|
|
// ConsumeFlag reads a long option or a short option at offset in a cluster.
|
|
func ConsumeFlag(lookup FlagLookup, token string, offset int) FlagConsumption {
|
|
var result FlagConsumption
|
|
if ClassifyFlagToken(token) == LongFlagToken {
|
|
result.Spelling, result.Value, result.Attached = SplitLongFlag(token)
|
|
result.Flag, result.Found = lookup.Find(result.Spelling)
|
|
result.NeedsNext = result.Found && !result.Flag.Boolean && !result.Attached
|
|
return result
|
|
}
|
|
result.Spelling = "-" + token[offset:offset+1]
|
|
result.Flag, result.Found = lookup.Find(result.Spelling)
|
|
if !result.Found {
|
|
return result
|
|
}
|
|
suffix := token[offset+1:]
|
|
if result.Flag.Boolean {
|
|
if strings.HasPrefix(suffix, "=") {
|
|
result.Attached, result.Value = true, suffix[1:]
|
|
} else if suffix != "" {
|
|
result.NextOffset = offset + 1
|
|
}
|
|
return result
|
|
}
|
|
if suffix == "" {
|
|
result.NeedsNext = true
|
|
return result
|
|
}
|
|
result.Attached = true
|
|
result.Value = strings.TrimPrefix(suffix, "=")
|
|
return result
|
|
}
|