Merge branch 'flags'
This commit is contained in:
commit
b9809e332b
9 changed files with 295 additions and 156 deletions
16
cat/cat.go
16
cat/cat.go
|
|
@ -22,19 +22,19 @@ const (
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
Filelist flags.Arg
|
Filelist flags.Arg
|
||||||
Base flags.Flag[string]
|
Base flags.String
|
||||||
KeepHeader flags.Flag[bool]
|
KeepHeader flags.Bool
|
||||||
KeepTitle flags.Flag[bool]
|
KeepTitle flags.Bool
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Arg{"", "filelist. accepts from stdin if not provided"},
|
flags.Arg{"", "filelist. accepts from stdin if not provided"},
|
||||||
flags.Flag[string]{"--base", "-b", "", "base directory prepended to files in filelist"},
|
flags.String{"--base", "-b", "", "base directory prepended to files in filelist"},
|
||||||
flags.Flag[bool]{"--keep-header", "-d", false, "preserve um headers in concatenated file. overrides --keep-title"},
|
flags.Bool{"--keep-header", "-d", false, "preserve um headers in concatenated file. overrides --keep-title"},
|
||||||
flags.Flag[bool]{"--keep-title", "-t", false, "preserve um titles in concatenated file"},
|
flags.Bool{"--keep-title", "-t", false, "preserve um titles in concatenated file"},
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
261
flags/flags.go
261
flags/flags.go
|
|
@ -1,139 +1,132 @@
|
||||||
package flags
|
package flags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/brtholomy/um/go/cmd"
|
"github.com/brtholomy/um/go/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const INTERNAL_ERR_PREFIX = "um internal err: ParseArgs"
|
const INTERNAL_ERR_PREFIX = "um internal err: ParseArgs"
|
||||||
|
|
||||||
|
// interface as func parameter
|
||||||
|
type Flag interface {
|
||||||
|
Set(string)
|
||||||
|
Valid([]string, int) bool
|
||||||
|
MaybeIncrement(int) int
|
||||||
|
Match(string, int, int) bool
|
||||||
|
IsSet() bool
|
||||||
|
IsHelp() bool
|
||||||
|
}
|
||||||
|
|
||||||
type Arg struct {
|
type Arg struct {
|
||||||
Val string
|
Val string
|
||||||
Help string
|
Help string
|
||||||
}
|
}
|
||||||
|
|
||||||
// type constraint
|
type String struct {
|
||||||
type Val interface {
|
|
||||||
string | bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// with generic Val field, instantiate like Flag[string]
|
|
||||||
// NOTE: struct inheritance would be easier, but Flag[bool] is much cooler than FlagBool.
|
|
||||||
// and actually more readable for the initOpts function, which serves as documentation.
|
|
||||||
// so the complexity seems to be all paid up front here in this pkg.
|
|
||||||
type Flag[T Val] struct {
|
|
||||||
Long string
|
Long string
|
||||||
Short string
|
Short string
|
||||||
Val T
|
Val string
|
||||||
Help string
|
Help string
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic Flag validity check
|
type Bool struct {
|
||||||
// NOTE: you cannot define a method on an instantiated type, only the generic
|
Long string
|
||||||
// https://www.reddit.com/r/golang/comments/1n6xasx/comment/nc3dbhd/
|
Short string
|
||||||
func (f Flag[Val]) IsSet() bool {
|
Val bool
|
||||||
// must do this weird dance: convert to empty interface, then cast:
|
Help string
|
||||||
switch any(f.Val).(type) {
|
|
||||||
case string:
|
|
||||||
return any(f.Val).(string) != ""
|
|
||||||
case bool:
|
|
||||||
return any(f.Val).(bool)
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Arg) IsSet() bool {
|
func (f *Arg) Set(arg string) {
|
||||||
|
f.Val = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Arg) Valid(args []string, i int) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Arg) Match(arg string, i, j int) bool {
|
||||||
|
return i == j && !hasDashPrefix(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Arg) IsSet() bool {
|
||||||
return f.Val != ""
|
return f.Val != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasDashPrefix(s string) bool {
|
func (f *Arg) IsHelp() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Arg) MaybeIncrement(i int) int {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) Set(arg string) {
|
||||||
|
f.Val = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) Valid(args []string, i int) bool {
|
||||||
|
return !missingValue(args, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) MaybeIncrement(i int) int {
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) Match(arg string, _, _ int) bool {
|
||||||
|
return arg == f.Long || arg == f.Short
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) IsSet() bool {
|
||||||
|
return f.Val != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *String) IsHelp() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) Set(arg string) {
|
||||||
|
f.Val = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) Match(arg string, _, _ int) bool {
|
||||||
|
return arg == f.Long || arg == f.Short
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) Valid(args []string, i int) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) MaybeIncrement(i int) int {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) IsSet() bool {
|
||||||
|
return f.Val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Bool) IsHelp() bool {
|
||||||
|
return f.Long == "--help"
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasDashPrefix(s string) bool {
|
||||||
return strings.HasPrefix(s, "-")
|
return strings.HasPrefix(s, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for non-dashed value ahead in the args slice
|
// check for non-dashed value ahead in the args slice
|
||||||
func missingValue(args []string, i int) bool {
|
func missingValue(args []string, i int) bool {
|
||||||
return i+1 == len(args) || HasDashPrefix(args[i+1])
|
return i+1 == len(args) || hasDashPrefix(args[i+1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no value ahead in the args, print error and exit
|
// expand incoming opts struct into a []Flag
|
||||||
func validValueOrExit(sub cmd.Subcommand, args []string, i int) {
|
|
||||||
if missingValue(args, i) {
|
|
||||||
log.Printf("um %s: %s needs a value assignment\n", sub, args[i])
|
|
||||||
log.Fatal("try: um [cmd] --help")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. validate a Flag[string] by looking ahead in args, if missing, os.Exit(1)
|
|
||||||
// 2. increment the i to skip that value and return
|
|
||||||
// 3. fetch that value and return
|
|
||||||
func ValidateIncrementFetchOrExit(sub cmd.Subcommand, args []string, i int) (int, string) {
|
|
||||||
validValueOrExit(sub, args, i)
|
|
||||||
return i + 1, args[i+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func HelpRequired(sub cmd.Subcommand, long string) {
|
|
||||||
log.Printf("um %s: %s is required", sub, long)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HelpInvalidArg(sub cmd.Subcommand, arg string) {
|
|
||||||
log.Printf("um %s: invalid argument: %s", sub, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// print out help string by reflecting over fields of provided opts struct
|
|
||||||
// and os.Exit(0)
|
|
||||||
func Help(sub cmd.Subcommand, summary string, opts any) {
|
|
||||||
v := reflect.ValueOf(opts)
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
t := v.Type()
|
|
||||||
|
|
||||||
// everything should go to either stderr or stdout:
|
|
||||||
w := tabwriter.NewWriter(os.Stderr, 0, 0, 3, ' ', 0)
|
|
||||||
positional := ""
|
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
value := v.Field(i)
|
|
||||||
switch value.Interface().(type) {
|
|
||||||
case Arg:
|
|
||||||
positional += fmt.Sprintf(" [%s]", strings.ToLower(field.Name))
|
|
||||||
fmt.Fprintf(w, "[%v]\tstring\t%v\n", strings.ToLower(field.Name), value.FieldByName("Help"))
|
|
||||||
case Flag[string], Flag[bool]:
|
|
||||||
if field.Name != "Help" {
|
|
||||||
positional += fmt.Sprintf(" [%s]", value.FieldByName("Long"))
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%v | %v\t%v\t%v\n",
|
|
||||||
value.FieldByName("Long"),
|
|
||||||
value.FieldByName("Short"),
|
|
||||||
value.FieldByName("Val").Type(),
|
|
||||||
value.FieldByName("Help"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("um %s%s\n\n", sub, positional)
|
|
||||||
log.Printf("%s\n\n", summary)
|
|
||||||
w.Flush()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the commandline args
|
|
||||||
//
|
|
||||||
// reflect on the incoming opts struct filled out with flags.Arg, flags.Flag[bool], flags.Flag[string]
|
|
||||||
// and fill their "Val" field appropriately.
|
|
||||||
//
|
//
|
||||||
// WARN: incoming opts must be a pointer!
|
// WARN: incoming opts must be a pointer!
|
||||||
// WORB: opts struct must list its Arg types first and in expected order
|
// WORB: opts struct must list its Arg types first and in expected order
|
||||||
// NOTE: uglier than the pretty switch statements I had per cmd, but much more convenient.
|
// NOTE: uglier than the pretty switch statements I had per cmd, but much more convenient.
|
||||||
func ParseArgs(sub cmd.Subcommand, summary string, args []string, opts any) {
|
func expandOpts(opts any) []Flag {
|
||||||
// immediately get the underlying value of the incoming pointer:
|
flags := []Flag{}
|
||||||
v := reflect.ValueOf(opts)
|
v := reflect.ValueOf(opts)
|
||||||
if v.Kind() == reflect.Ptr {
|
if v.Kind() == reflect.Ptr {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
|
|
@ -143,52 +136,50 @@ func ParseArgs(sub cmd.Subcommand, summary string, args []string, opts any) {
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
log.Fatalf("%s needs an underlying struct: %#v", INTERNAL_ERR_PREFIX, opts)
|
log.Fatalf("%s needs an underlying struct: %#v", INTERNAL_ERR_PREFIX, opts)
|
||||||
}
|
}
|
||||||
|
for j := 0; j < v.NumField(); j++ {
|
||||||
|
field := v.Field(j)
|
||||||
|
if field.Kind() != reflect.Struct {
|
||||||
|
log.Fatalf("%s needs a struct: %s", INTERNAL_ERR_PREFIX, field.Kind())
|
||||||
|
}
|
||||||
|
if !field.CanAddr() {
|
||||||
|
log.Fatalf("%s needs an addressable type: %s", INTERNAL_ERR_PREFIX, field.Type())
|
||||||
|
}
|
||||||
|
// NOTE: Addr() gets the underlying address:
|
||||||
|
if f, ok := field.Addr().Interface().(Flag); ok {
|
||||||
|
flags = append(flags, f)
|
||||||
|
} else {
|
||||||
|
log.Fatalf("%s needs a []Flag interface: %s", INTERNAL_ERR_PREFIX, field.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
// for all args, iterate through all opts fields and reflect on type
|
// internal for type safety testing
|
||||||
|
func parseArgsInternal(sub cmd.Subcommand, summary string, args []string, opts any, flags []Flag) {
|
||||||
argloop:
|
argloop:
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
arg := args[i]
|
arg := args[i]
|
||||||
|
for j, f := range flags {
|
||||||
for j := 0; j < v.NumField(); j++ {
|
if f.Match(arg, i, j) {
|
||||||
field := v.Field(j)
|
if f.IsHelp() {
|
||||||
if field.Kind() != reflect.Struct {
|
Help(sub, summary, opts)
|
||||||
log.Fatalf("%s needs a struct of flag types: %#v is type: %s", INTERNAL_ERR_PREFIX, field, field.Kind())
|
|
||||||
}
|
|
||||||
val := field.FieldByName("Val")
|
|
||||||
// NORT: FieldByName().String() won't panic if not present, as with Arg type, just prints "<invalid Value>"
|
|
||||||
long := field.FieldByName("Long").String()
|
|
||||||
short := field.FieldByName("Short").String()
|
|
||||||
if !val.CanSet() {
|
|
||||||
log.Fatalf("%s failed to set flag value: %#v", INTERNAL_ERR_PREFIX, field)
|
|
||||||
}
|
|
||||||
switch field.Interface().(type) {
|
|
||||||
case Arg:
|
|
||||||
// NOTE: we assume the opts struct lists its Arg types first and in order
|
|
||||||
if j == i && !HasDashPrefix(arg) {
|
|
||||||
val.SetString(arg)
|
|
||||||
continue argloop
|
|
||||||
}
|
}
|
||||||
case Flag[bool]:
|
if !f.Valid(args, i) {
|
||||||
if arg == long || arg == short {
|
HelpMissingAssignment(sub, arg)
|
||||||
val.SetBool(true)
|
|
||||||
if long == "--help" {
|
|
||||||
Help(sub, summary, opts)
|
|
||||||
}
|
|
||||||
continue argloop
|
|
||||||
}
|
}
|
||||||
case Flag[string]:
|
i = f.MaybeIncrement(i)
|
||||||
if arg == long || arg == short {
|
f.Set(args[i])
|
||||||
fetch := ""
|
continue argloop
|
||||||
i, fetch = ValidateIncrementFetchOrExit(sub, args, i)
|
|
||||||
val.SetString(fetch)
|
|
||||||
continue argloop
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalf("um internal err: unrecognized flag type in opts struct: %#v", field)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// BLAK: if we didn't skip ahead, arg didn't match:
|
// NOTE: if we didn't skip ahead, arg didn't match:
|
||||||
HelpInvalidArg(sub, arg)
|
HelpInvalidArg(sub, arg)
|
||||||
Help(sub, summary, opts)
|
Help(sub, summary, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assign values of args to opts struct using Flag interface methods
|
||||||
|
func ParseArgs(sub cmd.Subcommand, summary string, args []string, opts any) {
|
||||||
|
flags := expandOpts(opts)
|
||||||
|
parseArgsInternal(sub, summary, args, opts, flags)
|
||||||
|
}
|
||||||
|
|
|
||||||
85
flags/flags_test.go
Normal file
85
flags/flags_test.go
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CMD = "TEST"
|
||||||
|
SUMMARY = "SUMMARY"
|
||||||
|
)
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
Descriptor Arg
|
||||||
|
Tags Arg
|
||||||
|
Source String
|
||||||
|
Write Bool
|
||||||
|
Help Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func initOpts() options {
|
||||||
|
return options{
|
||||||
|
Arg{"", "midfix file descriptor"},
|
||||||
|
Arg{"", "tags to add to new file"},
|
||||||
|
String{"--source", "-s", "", "path to source list. reads from stdin if omitted."},
|
||||||
|
Bool{"--write", "-w", false, "write sorted list back to --key file"},
|
||||||
|
Bool{"--help", "-h", false, "show help"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseArgsInternal(t *testing.T) {
|
||||||
|
flags := []Flag{
|
||||||
|
&Arg{"", "midfix file descriptor"},
|
||||||
|
&Arg{"", "tags to add to new file"},
|
||||||
|
&String{"--source", "-s", "", "path to source list. reads from stdin if omitted."},
|
||||||
|
&Bool{"--write", "-w", false, "write sorted list back to --key file"},
|
||||||
|
&Bool{"--help", "-h", false, "show help"},
|
||||||
|
}
|
||||||
|
args := []string{"--source", "foo"}
|
||||||
|
parseArgsInternal(CMD, SUMMARY, args, initOpts(), flags)
|
||||||
|
assert.False(t, flags[0].IsSet())
|
||||||
|
assert.False(t, flags[0].IsHelp())
|
||||||
|
assert.True(t, flags[2].IsSet())
|
||||||
|
assert.True(t, flags[4].IsHelp())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: return errors from Help routines and print at top level, so I can test those cases
|
||||||
|
func TestParseArgsString(t *testing.T) {
|
||||||
|
args := []string{"--source", "foo"}
|
||||||
|
opts := initOpts()
|
||||||
|
ParseArgs(CMD, SUMMARY, args, &opts)
|
||||||
|
assert.Equal(t, opts.Source.Val, "foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseArgs(t *testing.T) {
|
||||||
|
opts := initOpts()
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
stringVal *string
|
||||||
|
stringWant string
|
||||||
|
boolVal *bool
|
||||||
|
boolWant bool
|
||||||
|
}{
|
||||||
|
{"arg", []string{"bar"}, &opts.Descriptor.Val, "bar", nil, false},
|
||||||
|
{"arg arg", []string{"bar", "foo"}, &opts.Tags.Val, "foo", nil, false},
|
||||||
|
{"--bool", []string{"--write"}, nil, "", &opts.Write.Val, true},
|
||||||
|
{"arg --bool", []string{"foo", "--write"}, &opts.Descriptor.Val, "foo", &opts.Write.Val, true},
|
||||||
|
{"--flag val", []string{"--source", "foo"}, &opts.Source.Val, "foo", nil, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ParseArgs(CMD, SUMMARY, tc.args, &opts)
|
||||||
|
if tc.boolVal != nil {
|
||||||
|
assert.True(t, *tc.boolVal)
|
||||||
|
}
|
||||||
|
if tc.stringVal != nil {
|
||||||
|
assert.Equal(t, *tc.stringVal, tc.stringWant)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
63
flags/help.go
Normal file
63
flags/help.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/brtholomy/um/go/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HelpRequired(sub cmd.Subcommand, long string) {
|
||||||
|
log.Printf("um %s: %s is required", sub, long)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpInvalidArg(sub cmd.Subcommand, arg string) {
|
||||||
|
log.Printf("um %s: invalid argument: %s", sub, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpMissingAssignment(sub cmd.Subcommand, arg string) {
|
||||||
|
log.Printf("um %s: %s needs a value assignment\n", sub, arg)
|
||||||
|
log.Fatal("try: um [cmd] --help")
|
||||||
|
}
|
||||||
|
|
||||||
|
// print out help string by reflecting over fields of provided opts struct
|
||||||
|
// and os.Exit(0)
|
||||||
|
func Help(sub cmd.Subcommand, summary string, opts any) {
|
||||||
|
v := reflect.ValueOf(opts)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
t := v.Type()
|
||||||
|
|
||||||
|
// everything should go to either stderr or stdout:
|
||||||
|
w := tabwriter.NewWriter(os.Stderr, 0, 0, 3, ' ', 0)
|
||||||
|
positional := ""
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
value := v.Field(i)
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case Arg:
|
||||||
|
positional += fmt.Sprintf(" [%s]", strings.ToLower(field.Name))
|
||||||
|
fmt.Fprintf(w, "[%v]\tstring\t%v\n", strings.ToLower(field.Name), value.FieldByName("Help"))
|
||||||
|
case String, Bool:
|
||||||
|
if field.Name != "Help" {
|
||||||
|
positional += fmt.Sprintf(" [%s]", value.FieldByName("Long"))
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%v | %v\t%v\t%v\n",
|
||||||
|
value.FieldByName("Long"),
|
||||||
|
value.FieldByName("Short"),
|
||||||
|
value.FieldByName("Val").Type(),
|
||||||
|
value.FieldByName("Help"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("um %s%s\n\n", sub, positional)
|
||||||
|
log.Printf("%s\n\n", summary)
|
||||||
|
w.Flush()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
@ -22,12 +22,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
4
mv/mv.go
4
mv/mv.go
|
|
@ -21,14 +21,14 @@ const (
|
||||||
type options struct {
|
type options struct {
|
||||||
Filename flags.Arg
|
Filename flags.Arg
|
||||||
Descriptor flags.Arg
|
Descriptor flags.Arg
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Arg{"", "old filename"},
|
flags.Arg{"", "old filename"},
|
||||||
flags.Arg{"", "new descriptor"},
|
flags.Arg{"", "new descriptor"},
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,14 @@ var fileRegexp *regexp.Regexp = regexp.MustCompile(FILE_REGEXP)
|
||||||
type options struct {
|
type options struct {
|
||||||
Descriptor flags.Arg
|
Descriptor flags.Arg
|
||||||
Tags flags.Arg
|
Tags flags.Arg
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Arg{"", "midfix file descriptor"},
|
flags.Arg{"", "midfix file descriptor"},
|
||||||
flags.Arg{"", "tags to add to new file"},
|
flags.Arg{"", "tags to add to new file"},
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
16
sort/sort.go
16
sort/sort.go
|
|
@ -18,18 +18,18 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
Source flags.Flag[string]
|
Source flags.String
|
||||||
Key flags.Flag[string]
|
Key flags.String
|
||||||
Write flags.Flag[bool]
|
Write flags.Bool
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Flag[string]{"--source", "-s", "", "path to source list. reads from stdin if omitted."},
|
flags.String{"--source", "-s", "", "path to source list. reads from stdin if omitted."},
|
||||||
flags.Flag[string]{"--key", "-k", "", "path to sort key"},
|
flags.String{"--key", "-k", "", "path to sort key"},
|
||||||
flags.Flag[bool]{"--write", "-w", false, "write sorted list back to --key file"},
|
flags.Bool{"--write", "-w", false, "write sorted list back to --key file"},
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
16
tag/tag.go
16
tag/tag.go
|
|
@ -15,19 +15,19 @@ const (
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
Query flags.Arg
|
Query flags.Arg
|
||||||
Date flags.Flag[string]
|
Date flags.String
|
||||||
Invert flags.Flag[bool]
|
Invert flags.Bool
|
||||||
Verbose flags.Flag[bool]
|
Verbose flags.Bool
|
||||||
Help flags.Flag[bool]
|
Help flags.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOpts() options {
|
func initOpts() options {
|
||||||
return options{
|
return options{
|
||||||
flags.Arg{"", "tag query: understands intersection '+' and union ','"},
|
flags.Arg{"", "tag query: understands intersection '+' and union ','"},
|
||||||
flags.Flag[string]{"--date", "-d", "", "date range in ISO 8601: YYYY.MM.DD[-YYYY.MM.DD]"},
|
flags.String{"--date", "-d", "", "date range in ISO 8601: YYYY.MM.DD[-YYYY.MM.DD]"},
|
||||||
flags.Flag[bool]{"--invert", "-i", false, "invert match"},
|
flags.Bool{"--invert", "-i", false, "invert match"},
|
||||||
flags.Flag[bool]{"--verbose", "-v", false, "print a verbose summary"},
|
flags.Bool{"--verbose", "-v", false, "print a verbose summary"},
|
||||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
flags.Bool{"--help", "-h", false, "show help"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue