flags : return HelpError and print at the callsite
This commit is contained in:
parent
b9809e332b
commit
d4015598be
9 changed files with 107 additions and 34 deletions
10
cat/cat.go
10
cat/cat.go
|
|
@ -1,6 +1,7 @@
|
||||||
package cat
|
package cat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -82,7 +83,14 @@ func cat(files []string, opts options) (string, error) {
|
||||||
|
|
||||||
func Cat(args []string) {
|
func Cat(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
files, err := pipe.FileListSplitMaybeStdin(opts.Filelist.Val)
|
files, err := pipe.FileListSplitMaybeStdin(opts.Filelist.Val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("um %s: %s", CMD, err)
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
|
|
||||||
|
|
@ -155,17 +155,19 @@ func expandOpts(opts any) []Flag {
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal for type safety testing
|
// internal for type safety testing
|
||||||
func parseArgsInternal(sub cmd.Subcommand, summary string, args []string, opts any, flags []Flag) {
|
func parseArgsInternal(sub cmd.Subcommand, summary string, args []string, opts any, flags []Flag) error {
|
||||||
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, f := range flags {
|
||||||
if f.Match(arg, i, j) {
|
if f.Match(arg, i, j) {
|
||||||
if f.IsHelp() {
|
if f.IsHelp() {
|
||||||
Help(sub, summary, opts)
|
// so Help.Val is true to avoid confusion:
|
||||||
|
f.Set(arg)
|
||||||
|
return Help(sub, summary, opts)
|
||||||
}
|
}
|
||||||
if !f.Valid(args, i) {
|
if !f.Valid(args, i) {
|
||||||
HelpMissingAssignment(sub, arg)
|
return HelpMissingAssignment(sub, arg)
|
||||||
}
|
}
|
||||||
i = f.MaybeIncrement(i)
|
i = f.MaybeIncrement(i)
|
||||||
f.Set(args[i])
|
f.Set(args[i])
|
||||||
|
|
@ -173,13 +175,13 @@ argloop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NOTE: if we didn't skip ahead, arg didn't match:
|
// NOTE: if we didn't skip ahead, arg didn't match:
|
||||||
HelpInvalidArg(sub, arg)
|
return HelpInvalidArg(sub, arg)
|
||||||
Help(sub, summary, opts)
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign values of args to opts struct using Flag interface methods
|
// assign values of args to opts struct using Flag interface methods
|
||||||
func ParseArgs(sub cmd.Subcommand, summary string, args []string, opts any) {
|
func ParseArgs(sub cmd.Subcommand, summary string, args []string, opts any) error {
|
||||||
flags := expandOpts(opts)
|
flags := expandOpts(opts)
|
||||||
parseArgsInternal(sub, summary, args, opts, flags)
|
return parseArgsInternal(sub, summary, args, opts, flags)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,20 @@ func TestParseArgsInternal(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return errors from Help routines and print at top level, so I can test those cases
|
func TestParseArgsHelp(t *testing.T) {
|
||||||
|
args := []string{"--help"}
|
||||||
|
opts := initOpts()
|
||||||
|
err := ParseArgs(CMD, SUMMARY, args, &opts)
|
||||||
|
assert.ErrorContains(t, err, "um TEST [descriptor] [tags] [--source] [--write]\n\nSUMMARY")
|
||||||
|
// we set the Val true although we don't expect to use it:
|
||||||
|
assert.True(t, opts.Help.Val)
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseArgsString(t *testing.T) {
|
func TestParseArgsString(t *testing.T) {
|
||||||
args := []string{"--source", "foo"}
|
args := []string{"--source", "foo"}
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
ParseArgs(CMD, SUMMARY, args, &opts)
|
err := ParseArgs(CMD, SUMMARY, args, &opts)
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, opts.Source.Val, "foo")
|
assert.Equal(t, opts.Source.Val, "foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +82,8 @@ func TestParseArgs(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
ParseArgs(CMD, SUMMARY, tc.args, &opts)
|
err := ParseArgs(CMD, SUMMARY, tc.args, &opts)
|
||||||
|
assert.NoError(t, err)
|
||||||
if tc.boolVal != nil {
|
if tc.boolVal != nil {
|
||||||
assert.True(t, *tc.boolVal)
|
assert.True(t, *tc.boolVal)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package flags
|
package flags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
@ -11,30 +10,43 @@ import (
|
||||||
"github.com/brtholomy/um/go/cmd"
|
"github.com/brtholomy/um/go/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HelpRequired(sub cmd.Subcommand, long string) {
|
type HelpError struct {
|
||||||
log.Printf("um %s: %s is required", sub, long)
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
func HelpInvalidArg(sub cmd.Subcommand, arg string) {
|
func (h HelpError) Error() string {
|
||||||
log.Printf("um %s: invalid argument: %s", sub, arg)
|
return h.message
|
||||||
}
|
}
|
||||||
|
|
||||||
func HelpMissingAssignment(sub cmd.Subcommand, arg string) {
|
func HelpRequired(sub cmd.Subcommand, long string) error {
|
||||||
log.Printf("um %s: %s needs a value assignment\n", sub, arg)
|
return HelpError{
|
||||||
log.Fatal("try: um [cmd] --help")
|
fmt.Sprintf("um %s: %s is required", sub, long),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpInvalidArg(sub cmd.Subcommand, arg string) error {
|
||||||
|
return HelpError{
|
||||||
|
fmt.Sprintf("um %s: invalid argument: %s", sub, arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HelpMissingAssignment(sub cmd.Subcommand, arg string) error {
|
||||||
|
return HelpError{
|
||||||
|
fmt.Sprintf("um %s: %s needs a value assignment", sub, arg),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print out help string by reflecting over fields of provided opts struct
|
// print out help string by reflecting over fields of provided opts struct
|
||||||
// and os.Exit(0)
|
// and os.Exit(0)
|
||||||
func Help(sub cmd.Subcommand, summary string, opts any) {
|
func Help(sub cmd.Subcommand, summary string, opts any) error {
|
||||||
v := reflect.ValueOf(opts)
|
v := reflect.ValueOf(opts)
|
||||||
if v.Kind() == reflect.Ptr {
|
if v.Kind() == reflect.Ptr {
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
|
|
||||||
// everything should go to either stderr or stdout:
|
buf := &bytes.Buffer{}
|
||||||
w := tabwriter.NewWriter(os.Stderr, 0, 0, 3, ' ', 0)
|
w := tabwriter.NewWriter(buf, 0, 0, 3, ' ', 0)
|
||||||
positional := ""
|
positional := ""
|
||||||
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
|
@ -56,8 +68,9 @@ func Help(sub cmd.Subcommand, summary string, opts any) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("um %s%s\n\n", sub, positional)
|
if _, err := buf.WriteString(fmt.Sprintf("um %s%s\n\n%s\n\n", sub, positional, summary)); err != nil {
|
||||||
log.Printf("%s\n\n", summary)
|
return err
|
||||||
w.Flush()
|
}
|
||||||
os.Exit(0)
|
w.Flush()
|
||||||
|
return HelpError{buf.String()}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,14 @@ func GlobLast(glob string) (string, error) {
|
||||||
|
|
||||||
func Last(args []string) {
|
func Last(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
|
|
||||||
s, err := GlobLast(GLOB)
|
s, err := GlobLast(GLOB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
10
mv/mv.go
10
mv/mv.go
|
|
@ -1,6 +1,7 @@
|
||||||
package mv
|
package mv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -89,7 +90,14 @@ func mv(opts options) error {
|
||||||
|
|
||||||
func Mv(args []string) {
|
func Mv(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
err := mv(opts)
|
err := mv(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("um %s: %v", CMD, err)
|
log.Fatalf("um %s: %v", CMD, err)
|
||||||
|
|
|
||||||
11
next/next.go
11
next/next.go
|
|
@ -1,6 +1,7 @@
|
||||||
package next
|
package next
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -99,8 +100,14 @@ func emacsNext(f string, tags string) error {
|
||||||
|
|
||||||
func Next(args []string) {
|
func Next(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
l, err := last.GlobLast(last.GLOB)
|
l, err := last.GlobLast(last.GLOB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("um %s: %v", CMD, err)
|
log.Fatalf("um %s: %v", CMD, err)
|
||||||
|
|
|
||||||
14
sort/sort.go
14
sort/sort.go
|
|
@ -1,6 +1,7 @@
|
||||||
package sort
|
package sort
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -69,11 +70,18 @@ func write(file string, content string) {
|
||||||
|
|
||||||
func Sort(args []string) {
|
func Sort(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
// BORK: by hand for now:
|
// BORK: by hand for now:
|
||||||
if !opts.Key.IsSet() {
|
if !opts.Key.IsSet() {
|
||||||
flags.HelpRequired(CMD, opts.Key.Long)
|
fmt.Println(flags.HelpRequired(CMD, opts.Key.Long))
|
||||||
flags.Help(CMD, SUMMARY, opts)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sslice, err := pipe.FileListSplitMaybeStdin(opts.Source.Val)
|
sslice, err := pipe.FileListSplitMaybeStdin(opts.Source.Val)
|
||||||
|
|
|
||||||
12
tag/tag.go
12
tag/tag.go
|
|
@ -1,6 +1,9 @@
|
||||||
package tag
|
package tag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/brtholomy/um/go/cmd"
|
"github.com/brtholomy/um/go/cmd"
|
||||||
|
|
@ -33,7 +36,14 @@ func initOpts() options {
|
||||||
|
|
||||||
func Tag(args []string) {
|
func Tag(args []string) {
|
||||||
opts := initOpts()
|
opts := initOpts()
|
||||||
flags.ParseArgs(CMD, SUMMARY, args, &opts)
|
if err := flags.ParseArgs(CMD, SUMMARY, args, &opts); err != nil {
|
||||||
|
var herr flags.HelpError
|
||||||
|
if errors.As(err, &herr) {
|
||||||
|
fmt.Println(herr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("um %s: %s", CMD, err)
|
||||||
|
}
|
||||||
|
|
||||||
queries := parseQuery(opts.Query.Val)
|
queries := parseQuery(opts.Query.Val)
|
||||||
entries := entriesGlobOrStdin(last.GLOB)
|
entries := entriesGlobOrStdin(last.GLOB)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue