From e1346691da2fdc1620292bce45f8ff6f213d2101 Mon Sep 17 00:00:00 2001 From: John Kerl Date: Mon, 16 Feb 2026 16:48:41 -0500 Subject: [PATCH] Improve error propagation (#1975) --- pkg/cli/errors.go | 13 +++ pkg/cli/verb_utils.go | 89 ++++++++++-------- pkg/climain/mlrcli_parse.go | 32 +++++-- pkg/entrypoint/entrypoint.go | 15 ++- pkg/lib/logger.go | 6 +- pkg/transformers/aaa_record_transformer.go | 7 +- pkg/transformers/altkv.go | 14 ++- pkg/transformers/bar.go | 53 +++++++---- pkg/transformers/bootstrap.go | 20 ++-- pkg/transformers/case.go | 20 ++-- pkg/transformers/cat.go | 25 +++-- pkg/transformers/check.go | 14 ++- pkg/transformers/clean_whitespace.go | 17 ++-- pkg/transformers/count.go | 25 +++-- pkg/transformers/count_similar.go | 28 +++--- pkg/transformers/cut.go | 23 ++--- pkg/transformers/decimate.go | 28 +++--- pkg/transformers/fill_down.go | 23 ++--- pkg/transformers/fill_empty.go | 20 ++-- pkg/transformers/flatten.go | 25 +++-- pkg/transformers/format_values.go | 30 +++--- pkg/transformers/fraction.go | 28 +++--- pkg/transformers/gap.go | 27 +++--- pkg/transformers/grep.go | 21 ++--- pkg/transformers/group_by.go | 17 ++-- pkg/transformers/group_like.go | 14 ++- pkg/transformers/having_fields.go | 51 +++++++---- pkg/transformers/head.go | 25 +++-- pkg/transformers/histogram.go | 49 ++++++---- pkg/transformers/join.go | 92 +++++++++++-------- pkg/transformers/json_parse.go | 20 ++-- pkg/transformers/json_stringify.go | 20 ++-- pkg/transformers/label.go | 20 ++-- pkg/transformers/latin1_to_utf8.go | 14 ++- pkg/transformers/merge_fields.go | 51 ++++++----- pkg/transformers/most_or_least_frequent.go | 38 ++++---- pkg/transformers/nest.go | 62 +++++++++---- pkg/transformers/nothing.go | 14 ++- pkg/transformers/put_or_filter.go | 44 ++++----- pkg/transformers/regularize.go | 14 ++- pkg/transformers/remove_empty_columns.go | 14 ++- pkg/transformers/rename.go | 20 ++-- pkg/transformers/reorder.go | 38 +++++--- pkg/transformers/repeat.go | 28 +++--- pkg/transformers/reshape.go | 47 ++++++---- pkg/transformers/sample.go | 28 +++--- pkg/transformers/sec2gmt.go | 17 ++-- pkg/transformers/sec2gmtdate.go | 17 ++-- pkg/transformers/seqgen.go | 35 ++++--- pkg/transformers/shuffle.go | 14 ++- pkg/transformers/skip_trivial_records.go | 14 ++- pkg/transformers/sort.go | 101 +++++++++++++++------ pkg/transformers/sort_within_records.go | 14 ++- pkg/transformers/sparsify.go | 25 +++-- pkg/transformers/split.go | 63 ++++++++----- pkg/transformers/stats1.go | 65 ++++++++----- pkg/transformers/stats2.go | 51 +++++------ pkg/transformers/step.go | 50 ++++++---- pkg/transformers/subs.go | 24 ++--- pkg/transformers/summary.go | 35 ++++--- pkg/transformers/surv.go | 32 +++---- pkg/transformers/tac.go | 14 ++- pkg/transformers/tail.go | 25 +++-- pkg/transformers/tee.go | 14 +-- pkg/transformers/template.go | 36 +++++--- pkg/transformers/top.go | 38 +++++--- pkg/transformers/unflatten.go | 25 +++-- pkg/transformers/uniq.go | 72 +++++++++------ pkg/transformers/unspace.go | 20 ++-- pkg/transformers/unsparsify.go | 25 +++-- pkg/transformers/utf8_to_latin1.go | 14 ++- 71 files changed, 1252 insertions(+), 911 deletions(-) create mode 100644 pkg/cli/errors.go diff --git a/pkg/cli/errors.go b/pkg/cli/errors.go new file mode 100644 index 000000000..9a41e064f --- /dev/null +++ b/pkg/cli/errors.go @@ -0,0 +1,13 @@ +package cli + +import "errors" + +// ErrHelpRequested is returned by verb ParseCLIFunc when -h or --help is used. +// The caller (CLI layer) should exit with code 0 after the verb has printed its +// usage to stdout. +var ErrHelpRequested = errors.New("help requested") + +// ErrUsagePrinted is returned by verb ParseCLIFunc when validation fails. The +// verb has already printed its usage to stderr. The caller should exit 1 +// without printing the error again. +var ErrUsagePrinted = errors.New("usage printed") diff --git a/pkg/cli/verb_utils.go b/pkg/cli/verb_utils.go index 1789d5163..463ff8ea4 100644 --- a/pkg/cli/verb_utils.go +++ b/pkg/cli/verb_utils.go @@ -1,70 +1,83 @@ // Utilities for Miller verbs to share for command-line parsing. +// These return error instead of os.Exit, so callers (e.g. transformer ParseCLIFunc) +// can propagate errors to the CLI entrypoint layer. package cli import ( "fmt" - "os" "strconv" "github.com/johnkerl/miller/v6/pkg/lib" ) -// For flags with values, e.g. ["-n" "10"], while we're looking at the "-n" this let us see if the "10" slot exists. -// The verb is nominally something from a ways earlier in args[]; the opt is nominally what's at args[argi-1]. -// So this function should be called with args[argi] pointing to the "10" slot. -func VerbCheckArgCount(verb string, opt string, args []string, argi int, argc int, n int) { +// VerbCheckArgCount returns an error if there aren't enough args remaining. +// For flags with values, e.g. ["-n" "10"], while we're looking at the "-n" +// this lets us see if the "10" slot exists. The verb is nominally something +// from a ways earlier in args[]; the opt is nominally what's at args[argi-1]. +// This function should be called with args[argi] pointing to the "10" slot. +func VerbCheckArgCount(verb string, opt string, args []string, argi int, argc int, n int) error { if (argc - argi) < n { - fmt.Fprintf(os.Stderr, "%s %s: option \"%s\" missing argument(s).\n", - "mlr", verb, opt, - ) - os.Exit(1) + return fmt.Errorf("%s %s: option \"%s\" missing argument(s)", "mlr", verb, opt) } + return nil } -// E.g. with ["-f", "a,b,c"], makes sure there is something in the "a,b,c" position, and returns it. -func VerbGetStringArgOrDie(verb string, opt string, args []string, pargi *int, argc int) string { - VerbCheckArgCount(verb, opt, args, *pargi, argc, 1) +// VerbGetStringArg ensures there is something in the value position and returns it. +// E.g. with ["-f", "a,b,c"], returns "a,b,c". +func VerbGetStringArg(verb string, opt string, args []string, pargi *int, argc int) (string, error) { + if err := VerbCheckArgCount(verb, opt, args, *pargi, argc, 1); err != nil { + return "", err + } retval := args[*pargi] *pargi += 1 - return retval + return retval, nil } -// E.g. with ["-f", "a,b,c"], makes sure there is something in the "a,b,c" position, -// splits it on commas, and returns it. -func VerbGetStringArrayArgOrDie(verb string, opt string, args []string, pargi *int, argc int) []string { - stringArg := VerbGetStringArgOrDie(verb, opt, args, pargi, argc) - return lib.SplitString(stringArg, ",") +// VerbGetStringArrayArg ensures there is something in the value position, +// splits it on commas, and returns it. E.g. with ["-f", "a,b,c"], returns ["a","b","c"]. +func VerbGetStringArrayArg(verb string, opt string, args []string, pargi *int, argc int) ([]string, error) { + stringArg, err := VerbGetStringArg(verb, opt, args, pargi, argc) + if err != nil { + return nil, err + } + return lib.SplitString(stringArg, ","), nil } -// E.g. with ["-n", "10"], makes sure there is something in the "10" position, -// scans it as int, and returns it. -func VerbGetIntArgOrDie(verb string, opt string, args []string, pargi *int, argc int) int64 { +// VerbErrorf returns an error prefixed with "mlr {verb}: " so the entrypoint +// can print it without double-prefixing. +func VerbErrorf(verb, format string, args ...interface{}) error { + return fmt.Errorf("mlr "+verb+": "+format, args...) +} + +// VerbGetIntArg ensures there is something in the value position and parses it as int64. +// E.g. with ["-n", "10"], returns 10. +func VerbGetIntArg(verb string, opt string, args []string, pargi *int, argc int) (int64, error) { flag := args[*pargi] - stringArg := VerbGetStringArgOrDie(verb, opt, args, pargi, argc) + stringArg, err := VerbGetStringArg(verb, opt, args, pargi, argc) + if err != nil { + return 0, err + } retval, err := strconv.ParseInt(stringArg, 10, 64) if err != nil { - fmt.Fprintf(os.Stderr, - "%s %s: could not scan flag \"%s\" argument \"%s\" as int.\n", - "mlr", verb, flag, stringArg, - ) - os.Exit(1) + return 0, fmt.Errorf("%s %s: could not scan flag \"%s\" argument \"%s\" as int", + "mlr", verb, flag, stringArg) } - return retval + return retval, nil } -// E.g. with ["-n", "10.3"], makes sure there is something in the "10.3" -// position, scans it as float, and returns it. -func VerbGetFloatArgOrDie(verb string, opt string, args []string, pargi *int, argc int) float64 { +// VerbGetFloatArg ensures there is something in the value position and parses it as float64. +// E.g. with ["-n", "10.3"], returns 10.3. +func VerbGetFloatArg(verb string, opt string, args []string, pargi *int, argc int) (float64, error) { flag := args[*pargi] - stringArg := VerbGetStringArgOrDie(verb, opt, args, pargi, argc) + stringArg, err := VerbGetStringArg(verb, opt, args, pargi, argc) + if err != nil { + return 0, err + } retval, err := strconv.ParseFloat(stringArg, 64) if err != nil { - fmt.Fprintf(os.Stderr, - "%s %s: could not scan flag \"%s\" argument \"%s\" as float.\n", - "mlr", verb, flag, stringArg, - ) - os.Exit(1) + return 0, fmt.Errorf("%s %s: could not scan flag \"%s\" argument \"%s\" as float", + "mlr", verb, flag, stringArg) } - return retval + return retval, nil } diff --git a/pkg/climain/mlrcli_parse.go b/pkg/climain/mlrcli_parse.go index ada3e250f..5963fc985 100644 --- a/pkg/climain/mlrcli_parse.go +++ b/pkg/climain/mlrcli_parse.go @@ -69,6 +69,7 @@ package climain import ( + "errors" "fmt" "os" @@ -205,16 +206,24 @@ func parseCommandLinePassOne( os.Exit(1) } - // It's up to the parse func to print its usage, and exit 1, on - // CLI-parse failure. Also note: this assumes main reader/writer opts - // are all parsed *before* transformer parse-CLI methods are invoked. - transformer := transformerSetup.ParseCLIFunc( + // ParseCLIFunc returns (nil, nil) on pass-one success, (nil, err) on failure. + transformer, err := transformerSetup.ParseCLIFunc( &argi, argc, args, options, false, // false for first pass of CLI-parse, true for second pass -- this is the first pass ) + if err != nil { + if errors.Is(err, cli.ErrHelpRequested) { + os.Exit(0) + } + if errors.Is(err, cli.ErrUsagePrinted) { + os.Exit(1) + } + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) + os.Exit(1) + } // For pass one we want the verbs to identify the arg-sequences // they own within the command line, but not construct // transformers. @@ -348,15 +357,22 @@ func parseCommandLinePassTwo( transformerSetup := transformers.LookUp(args[0]) lib.InternalCodingErrorIf(transformerSetup == nil) - // It's up to the parse func to print its usage, and exit 1, on - // CLI-parse failure. - transformer := transformerSetup.ParseCLIFunc( + transformer, err := transformerSetup.ParseCLIFunc( &argi, argc, args, options, - true, // false for first pass of CLI-parse, true for second pass -- this is the first pass + true, // false for first pass of CLI-parse, true for second pass -- this is pass two ) + if err != nil { + if errors.Is(err, cli.ErrHelpRequested) { + os.Exit(0) + } + if errors.Is(err, cli.ErrUsagePrinted) { + os.Exit(1) + } + return nil, nil, err + } // Unparsable verb-setups should have been found in pass one. lib.InternalCodingErrorIf(transformer == nil) // Make sure we consumed the entire verb sequence as parsed by pass one. diff --git a/pkg/entrypoint/entrypoint.go b/pkg/entrypoint/entrypoint.go index 21ee389cc..5d106a3a6 100644 --- a/pkg/entrypoint/entrypoint.go +++ b/pkg/entrypoint/entrypoint.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path" + "strings" "github.com/johnkerl/miller/v6/pkg/auxents" "github.com/johnkerl/miller/v6/pkg/cli" @@ -41,7 +42,7 @@ func Main() MainReturn { options, recordTransformers, err := climain.ParseCommandLine(os.Args) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) + printError(err) os.Exit(1) } @@ -51,7 +52,7 @@ func Main() MainReturn { err = processFilesInPlace(options) } if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) + printError(err) os.Exit(1) } @@ -60,6 +61,16 @@ func Main() MainReturn { } } +// printError prints err to stderr. Errors that already start with "mlr " (e.g. +// "mlr stats1: ...") are printed as-is to avoid double-prefixing. +func printError(err error) { + if strings.HasPrefix(err.Error(), "mlr ") { + fmt.Fprintf(os.Stderr, "%v\n", err) + } else { + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) + } +} + // processToStdout is normal processing without mlr -I. func processToStdout( diff --git a/pkg/lib/logger.go b/pkg/lib/logger.go index 5868dccf5..99a26d431 100644 --- a/pkg/lib/logger.go +++ b/pkg/lib/logger.go @@ -7,8 +7,10 @@ import ( "runtime" ) -// InternalCodingErrorIf is a lookalike for C's __FILE__ and __LINE__ printing, -// with exit 1 if the condition is true. +// InternalCodingErrorIf reports an internal bug and exits. Use only for +// "should never happen" conditions (assertions), not for user or I/O errors. +// This is intentionally an exit helper: these indicate programmer error, +// and the codebase prefers returning error for recoverable failures. func InternalCodingErrorIf(condition bool) { if !condition { return diff --git a/pkg/transformers/aaa_record_transformer.go b/pkg/transformers/aaa_record_transformer.go index 9067e38ee..298129db9 100644 --- a/pkg/transformers/aaa_record_transformer.go +++ b/pkg/transformers/aaa_record_transformer.go @@ -36,13 +36,18 @@ type TransformerUsageFunc func( ostream *os.File, ) +// TransformerParseCLIFunc parses verb options from the CLI. Returns (nil, nil) on +// success for pass one (doConstruct=false). Returns (transformer, nil) on success +// for pass two (doConstruct=true). Returns (nil, cli.ErrHelpRequested) when -h/--help +// was used (caller should exit 0; usage already printed). Returns (nil, err) on +// parse or constructor failure (caller should print and exit 1). type TransformerParseCLIFunc func( pargi *int, argc int, args []string, mainOptions *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer +) (RecordTransformer, error) type TransformerSetup struct { Verb string diff --git a/pkg/transformers/altkv.go b/pkg/transformers/altkv.go index f842460e3..42a099ce1 100644 --- a/pkg/transformers/altkv.go +++ b/pkg/transformers/altkv.go @@ -35,7 +35,7 @@ func transformerAltkvParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -53,25 +53,23 @@ func transformerAltkvParseCLI( if opt == "-h" || opt == "--help" { transformerAltkvUsage(os.Stdout) - os.Exit(0) - + return nil, cli.ErrHelpRequested } transformerAltkvUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: option \"%s\" not recognized", "mlr", verbNameAltkv, opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerAltkv() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerAltkv struct { diff --git a/pkg/transformers/bar.go b/pkg/transformers/bar.go index b1361396c..b9b14c2dd 100644 --- a/pkg/transformers/bar.go +++ b/pkg/transformers/bar.go @@ -54,7 +54,7 @@ func transformerBarParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -62,6 +62,7 @@ func transformerBarParseCLI( argi++ // Parse local flags + var err error var fieldNames []string = nil lo := barDefaultLo hi := barDefaultHi @@ -83,42 +84,61 @@ func transformerBarParseCLI( if opt == "-h" || opt == "--help" { transformerBarUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--lo" { - lo = cli.VerbGetFloatArgOrDie(verb, opt, args, &argi, argc) + lo, err = cli.VerbGetFloatArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-w" { - width = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + width, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--hi" { - hi = cli.VerbGetFloatArgOrDie(verb, opt, args, &argi, argc) + hi, err = cli.VerbGetFloatArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-c" { - fillString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fillString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-x" { - oobString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + oobString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-b" { - blankString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + blankString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--auto" { doAuto = true } else { - transformerBarUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fieldNames == nil { - transformerBarUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerBar( @@ -132,11 +152,10 @@ func transformerBarParseCLI( blankString, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerBar struct { diff --git a/pkg/transformers/bootstrap.go b/pkg/transformers/bootstrap.go index 1f196d6e4..a21c30b0a 100644 --- a/pkg/transformers/bootstrap.go +++ b/pkg/transformers/bootstrap.go @@ -41,13 +41,14 @@ func transformerBootstrapParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ + var err error nout := int64(-1) for argi < argc /* variable increment: 1 or 2 depending on flag */ { @@ -62,29 +63,30 @@ func transformerBootstrapParseCLI( if opt == "-h" || opt == "--help" { transformerBootstrapUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - nout = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + nout, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerBootstrapUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerBootstrap(nout) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerBootstrap struct { diff --git a/pkg/transformers/case.go b/pkg/transformers/case.go index 7faafc6ad..632c93880 100644 --- a/pkg/transformers/case.go +++ b/pkg/transformers/case.go @@ -53,13 +53,14 @@ func transformerCaseParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ + var err error which := "keys_and_values" style := e_UNSPECIFIED_CASE var fieldNames []string = nil @@ -76,7 +77,7 @@ func transformerCaseParseCLI( if opt == "-h" || opt == "--help" { transformerCaseUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-k" { which = "keys_only" @@ -85,7 +86,10 @@ func transformerCaseParseCLI( which = "values_only" } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-u" { style = e_UPPER_CASE @@ -97,23 +101,21 @@ func transformerCaseParseCLI( style = e_TITLE_CASE } else { - transformerCaseUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCase(which, fieldNames, style) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type caserFuncT func(input string) string diff --git a/pkg/transformers/cat.go b/pkg/transformers/cat.go index 7601cbcd8..146511821 100644 --- a/pkg/transformers/cat.go +++ b/pkg/transformers/cat.go @@ -39,13 +39,14 @@ func transformerCatParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ + var err error // Parse local flags doCounters := false counterFieldName := "" @@ -65,16 +66,22 @@ func transformerCatParseCLI( if opt == "-h" || opt == "--help" { transformerCatUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { counterFieldName = "n" } else if opt == "-N" { - counterFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + counterFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--filename" { doFileName = true @@ -83,14 +90,13 @@ func transformerCatParseCLI( doFileNum = true } else { - transformerCatUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCat( @@ -101,11 +107,10 @@ func transformerCatParseCLI( doFileNum, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCat struct { diff --git a/pkg/transformers/check.go b/pkg/transformers/check.go index e2571040d..5af3c2f5d 100644 --- a/pkg/transformers/check.go +++ b/pkg/transformers/check.go @@ -38,7 +38,7 @@ func transformerCheckParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -56,25 +56,23 @@ func transformerCheckParseCLI( if opt == "-h" || opt == "--help" { transformerCheckUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerCheckUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameCheck, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCheck() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCheck struct { diff --git a/pkg/transformers/clean_whitespace.go b/pkg/transformers/clean_whitespace.go index 2c4a583fb..7770864f8 100644 --- a/pkg/transformers/clean_whitespace.go +++ b/pkg/transformers/clean_whitespace.go @@ -44,7 +44,7 @@ func transformerCleanWhitespaceParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { doKeys := true doValues := true @@ -65,7 +65,7 @@ func transformerCleanWhitespaceParseCLI( if opt == "-h" || opt == "--help" { transformerCleanWhitespaceUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-k" || opt == "--keys-only" { doKeys = true @@ -75,19 +75,17 @@ func transformerCleanWhitespaceParseCLI( doValues = true } else { - transformerCleanWhitespaceUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameCleanWhitespace, "option \"%s\" not recognized", opt) } } if !doKeys && !doValues { - transformerCleanWhitespaceUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameCleanWhitespace, "-k or -v is required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCleanWhitespace( @@ -95,11 +93,10 @@ func transformerCleanWhitespaceParseCLI( doValues, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCleanWhitespace struct { diff --git a/pkg/transformers/count.go b/pkg/transformers/count.go index 237cf51e1..c6dd60efb 100644 --- a/pkg/transformers/count.go +++ b/pkg/transformers/count.go @@ -40,7 +40,7 @@ func transformerCountParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -51,6 +51,7 @@ func transformerCountParseCLI( showCountsOnly := false outputFieldName := "count" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -63,26 +64,31 @@ func transformerCountParseCLI( if opt == "-h" || opt == "--help" { transformerCountUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-n" { showCountsOnly = true } else if opt == "-o" { - outputFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerCountUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCount( @@ -91,11 +97,10 @@ func transformerCountParseCLI( outputFieldName, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCount struct { diff --git a/pkg/transformers/count_similar.go b/pkg/transformers/count_similar.go index fee147674..40ae9f173 100644 --- a/pkg/transformers/count_similar.go +++ b/pkg/transformers/count_similar.go @@ -38,7 +38,7 @@ func transformerCountSimilarParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -48,6 +48,7 @@ func transformerCountSimilarParseCLI( var groupByFieldNames []string = nil counterFieldName := "count" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -60,28 +61,32 @@ func transformerCountSimilarParseCLI( if opt == "-h" || opt == "--help" { transformerCountSimilarUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-o" { - counterFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + counterFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerCountSimilarUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if groupByFieldNames == nil { - transformerCountSimilarUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-g field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCountSimilar( @@ -89,11 +94,10 @@ func transformerCountSimilarParseCLI( counterFieldName, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCountSimilar struct { diff --git a/pkg/transformers/cut.go b/pkg/transformers/cut.go index bf588334a..2a1453e36 100644 --- a/pkg/transformers/cut.go +++ b/pkg/transformers/cut.go @@ -52,13 +52,14 @@ func transformerCutParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ + var err error var fieldNames []string = nil doArgOrder := false doComplement := false @@ -76,10 +77,13 @@ func transformerCutParseCLI( if opt == "-h" || opt == "--help" { transformerCutUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-o" { doArgOrder = true @@ -94,19 +98,17 @@ func transformerCutParseCLI( doRegexes = true } else { - transformerCutUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fieldNames == nil { - transformerCutUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerCut( @@ -116,11 +118,10 @@ func transformerCutParseCLI( doRegexes, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerCut struct { diff --git a/pkg/transformers/decimate.go b/pkg/transformers/decimate.go index 9a5790019..902a8b28b 100644 --- a/pkg/transformers/decimate.go +++ b/pkg/transformers/decimate.go @@ -37,13 +37,14 @@ func transformerDecimateParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ + var err error decimateCount := int64(10) atStart := false atEnd := false @@ -61,13 +62,15 @@ func transformerDecimateParseCLI( if opt == "-h" || opt == "--help" { transformerDecimateUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - decimateCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + decimateCount, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } if decimateCount <= 0 { - transformerDecimateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-n must be positive") } } else if opt == "-b" { @@ -77,17 +80,19 @@ func transformerDecimateParseCLI( atEnd = true } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerDecimateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerDecimate( @@ -97,11 +102,10 @@ func transformerDecimateParseCLI( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerDecimate struct { diff --git a/pkg/transformers/fill_down.go b/pkg/transformers/fill_down.go index a53b3fb08..6dd13064a 100644 --- a/pkg/transformers/fill_down.go +++ b/pkg/transformers/fill_down.go @@ -44,7 +44,7 @@ func transformerFillDownParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -55,6 +55,7 @@ func transformerFillDownParseCLI( doAll := false onlyIfAbsent := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -67,10 +68,13 @@ func transformerFillDownParseCLI( if opt == "-h" || opt == "--help" { transformerFillDownUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fillDownFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fillDownFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--all" { doAll = true @@ -82,19 +86,17 @@ func transformerFillDownParseCLI( onlyIfAbsent = true } else { - transformerFillDownUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fillDownFieldNames == nil && !doAll { - transformerFillDownUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names or --all required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerFillDown( @@ -103,11 +105,10 @@ func transformerFillDownParseCLI( onlyIfAbsent, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerFillDown struct { diff --git a/pkg/transformers/fill_empty.go b/pkg/transformers/fill_empty.go index ef8eaf7c3..b24cb76c1 100644 --- a/pkg/transformers/fill_empty.go +++ b/pkg/transformers/fill_empty.go @@ -36,7 +36,7 @@ func transformerFillEmptyParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -46,6 +46,7 @@ func transformerFillEmptyParseCLI( fillString := defaultFillEmptyString inferType := true + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -58,32 +59,33 @@ func transformerFillEmptyParseCLI( if opt == "-h" || opt == "--help" { transformerFillEmptyUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-v" { - fillString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fillString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-S" { inferType = false } else { - transformerFillEmptyUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerFillEmpty(fillString, inferType) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerFillEmpty struct { diff --git a/pkg/transformers/flatten.go b/pkg/transformers/flatten.go index 90a0d3bc4..cacaf21af 100644 --- a/pkg/transformers/flatten.go +++ b/pkg/transformers/flatten.go @@ -39,7 +39,7 @@ func transformerFlattenParseCLI( args []string, options *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -49,6 +49,7 @@ func transformerFlattenParseCLI( oFlatSep := "" // means take it from the record context var fieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -61,23 +62,28 @@ func transformerFlattenParseCLI( if opt == "-h" || opt == "--help" { transformerFlattenUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-s" { - oFlatSep = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + oFlatSep, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerFlattenUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerFlatten( @@ -86,11 +92,10 @@ func transformerFlattenParseCLI( fieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerFlatten struct { diff --git a/pkg/transformers/format_values.go b/pkg/transformers/format_values.go index 452640738..0052e4c6e 100644 --- a/pkg/transformers/format_values.go +++ b/pkg/transformers/format_values.go @@ -63,7 +63,7 @@ func transformerFormatValuesParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -75,6 +75,7 @@ func transformerFormatValuesParseCLI( floatFormat := defaultFormatValuesFloatFormat coerceIntToFloat := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -87,26 +88,34 @@ func transformerFormatValuesParseCLI( if opt == "-h" || opt == "--help" { transformerFormatValuesUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-s" { - stringFormat = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + stringFormat, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-i" { - intFormat = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + intFormat, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - floatFormat = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + floatFormat, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-n" { coerceIntToFloat = true } else { - transformerFormatValuesUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerFormatValues( @@ -116,11 +125,10 @@ func transformerFormatValuesParseCLI( coerceIntToFloat, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerFormatValues struct { diff --git a/pkg/transformers/fraction.go b/pkg/transformers/fraction.go index dbb8f85ed..bbd685f30 100644 --- a/pkg/transformers/fraction.go +++ b/pkg/transformers/fraction.go @@ -55,7 +55,7 @@ func transformerFractionParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -68,6 +68,7 @@ func transformerFractionParseCLI( doPercents := false doCumu := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -80,13 +81,19 @@ func transformerFractionParseCLI( if opt == "-h" || opt == "--help" { transformerFractionUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fractionFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fractionFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-p" { doPercents = true @@ -95,19 +102,17 @@ func transformerFractionParseCLI( doCumu = true } else { - transformerFractionUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fractionFieldNames == nil { - transformerFractionUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerFraction( @@ -117,11 +122,10 @@ func transformerFractionParseCLI( doCumu, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerFraction struct { diff --git a/pkg/transformers/gap.go b/pkg/transformers/gap.go index 4ffb705ea..e7b18d18b 100644 --- a/pkg/transformers/gap.go +++ b/pkg/transformers/gap.go @@ -40,7 +40,7 @@ func transformerGapParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -50,6 +50,7 @@ func transformerGapParseCLI( gapCount := int64(-1) var groupByFieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -62,28 +63,33 @@ func transformerGapParseCLI( if opt == "-h" || opt == "--help" { transformerGapUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - gapCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + gapCount, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerGapUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if gapCount == -1 && groupByFieldNames == nil { transformerGapUsage(os.Stderr) - os.Exit(1) + return nil, cli.ErrUsagePrinted } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerGap( @@ -91,11 +97,10 @@ func transformerGapParseCLI( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerGap struct { diff --git a/pkg/transformers/grep.go b/pkg/transformers/grep.go index 293a76f35..ed1356b46 100644 --- a/pkg/transformers/grep.go +++ b/pkg/transformers/grep.go @@ -51,7 +51,7 @@ func transformerGrepParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -74,7 +74,7 @@ func transformerGrepParseCLI( if opt == "-h" || opt == "--help" { transformerGrepUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-i" { ignoreCase = true @@ -86,15 +86,13 @@ func transformerGrepParseCLI( valuesOnly = true } else { - transformerGrepUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } // Get the regex from the command line if argi >= argc { - transformerGrepUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "pattern required") } pattern := args[argi] argi++ @@ -106,14 +104,12 @@ func transformerGrepParseCLI( // TODO: maybe CompilePOSIX regexp, err := regexp.Compile(pattern) if err != nil { - fmt.Fprintf(os.Stderr, "%s %s: couldn't compile regex \"%s\"\n", - "mlr", verb, pattern) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "invalid regex \"%s\": %w", pattern, err) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerGrep( @@ -122,11 +118,10 @@ func transformerGrepParseCLI( valuesOnly, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerGrep struct { diff --git a/pkg/transformers/group_by.go b/pkg/transformers/group_by.go index 06313c319..dea289233 100644 --- a/pkg/transformers/group_by.go +++ b/pkg/transformers/group_by.go @@ -34,7 +34,7 @@ func transformerGroupByParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -52,35 +52,32 @@ func transformerGroupByParseCLI( if opt == "-h" || opt == "--help" { transformerGroupByUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerGroupByUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameGroupBy, "option \"%s\" not recognized", opt) } // Get the group-by field names from the command line if argi >= argc { - transformerGroupByUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameGroupBy, "group-by field names required") } groupByFieldNames := lib.SplitString(args[argi], ",") argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerGroupBy( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerGroupBy struct { diff --git a/pkg/transformers/group_like.go b/pkg/transformers/group_like.go index 6b2d0cab5..edec0d76c 100644 --- a/pkg/transformers/group_like.go +++ b/pkg/transformers/group_like.go @@ -34,7 +34,7 @@ func transformerGroupLikeParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -52,25 +52,23 @@ func transformerGroupLikeParseCLI( if opt == "-h" || opt == "--help" { transformerGroupLikeUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerGroupLikeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameGroupLike, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerGroupLike() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerGroupLike struct { diff --git a/pkg/transformers/having_fields.go b/pkg/transformers/having_fields.go index 35bc5b9b9..780fcd082 100644 --- a/pkg/transformers/having_fields.go +++ b/pkg/transformers/having_fields.go @@ -61,7 +61,7 @@ func transformerHavingFieldsParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { havingFieldsCriterion := havingFieldsCriterionUnspecified var fieldNames []string = nil @@ -72,6 +72,7 @@ func transformerHavingFieldsParseCLI( verb := args[argi] argi++ + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -84,56 +85,71 @@ func transformerHavingFieldsParseCLI( if opt == "-h" || opt == "--help" { transformerHavingFieldsUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "--at-least" { havingFieldsCriterion = havingFieldsAtLeast - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } regexString = "" } else if opt == "--which-are" { havingFieldsCriterion = havingFieldsWhichAre - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } regexString = "" } else if opt == "--at-most" { havingFieldsCriterion = havingFieldsAtMost - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } regexString = "" } else if opt == "--all-matching" { havingFieldsCriterion = havingAllFieldsMatching - regexString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + regexString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } fieldNames = nil } else if opt == "--any-matching" { havingFieldsCriterion = havingAnyFieldsMatching - regexString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + regexString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } fieldNames = nil } else if opt == "--none-matching" { havingFieldsCriterion = havingNoFieldsMatching - regexString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + regexString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } fieldNames = nil } else { - transformerHavingFieldsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if havingFieldsCriterion == havingFieldsCriterionUnspecified { - transformerHavingFieldsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-a, -b, -n, or --any-matching/--all-matching/--none-matching is required") } if fieldNames == nil && regexString == "" { - transformerHavingFieldsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "field names or regex required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerHavingFields( @@ -142,11 +158,10 @@ func transformerHavingFieldsParseCLI( regexString, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerHavingFields struct { diff --git a/pkg/transformers/head.go b/pkg/transformers/head.go index c6cd9fbe7..307f090c3 100644 --- a/pkg/transformers/head.go +++ b/pkg/transformers/head.go @@ -37,7 +37,7 @@ func transformerHeadParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -59,10 +59,14 @@ func transformerHeadParseCLI( if opt == "-h" || opt == "--help" { transformerHeadUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - headCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + n, err := cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + headCount = n // This is a bit of a hack. In our Getoptify routine we preprocess // the command line sending '-xyz' to '-x -y -z', but leaving @@ -75,17 +79,21 @@ func transformerHeadParseCLI( } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + names, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + groupByFieldNames = names } else { transformerHeadUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: option \"%s\" not recognized", "mlr", verb, opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerHead( @@ -93,11 +101,10 @@ func transformerHeadParseCLI( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerHead struct { diff --git a/pkg/transformers/histogram.go b/pkg/transformers/histogram.go index 4ac30d31c..aaae60112 100644 --- a/pkg/transformers/histogram.go +++ b/pkg/transformers/histogram.go @@ -44,7 +44,7 @@ func transformerHistogramParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -59,6 +59,7 @@ func transformerHistogramParseCLI( doAuto := false outputPrefix := "" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -71,50 +72,61 @@ func transformerHistogramParseCLI( if opt == "-h" || opt == "--help" { transformerHistogramUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - valueFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--lo" { - lo = cli.VerbGetFloatArgOrDie(verb, opt, args, &argi, argc) + lo, err = cli.VerbGetFloatArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--nbins" { - nbins = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + nbins, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--hi" { - hi = cli.VerbGetFloatArgOrDie(verb, opt, args, &argi, argc) + hi, err = cli.VerbGetFloatArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--auto" { doAuto = true } else if opt == "-o" { - outputPrefix = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputPrefix, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerHistogramUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if valueFieldNames == nil { - transformerHistogramUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } if nbins <= 0 { - transformerHistogramUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "number of bins must be positive") } if lo == hi && !doAuto { - transformerHistogramUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "lo and hi must differ, or use --auto") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerHistogram( @@ -126,11 +138,10 @@ func transformerHistogramParseCLI( outputPrefix, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } const histogramVectorInitialSize = 1024 diff --git a/pkg/transformers/join.go b/pkg/transformers/join.go index 7c59de2eb..d4d1189e6 100644 --- a/pkg/transformers/join.go +++ b/pkg/transformers/join.go @@ -136,7 +136,7 @@ func transformerJoinParseCLI( args []string, mainOptions *cli.TOptions, // Options for the right-files doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -151,6 +151,7 @@ func transformerJoinParseCLI( opts.joinFlagOptions = *mainOptions // struct copy } + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -163,36 +164,63 @@ func transformerJoinParseCLI( if opt == "-h" || opt == "--help" { transformerJoinUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "--prepipe" { - opts.prepipe = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + opts.prepipe, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } opts.prepipeIsRaw = false } else if opt == "--prepipex" { - opts.prepipe = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + opts.prepipe, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } opts.prepipeIsRaw = true } else if opt == "-f" { - opts.leftFileName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + opts.leftFileName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-j" { - opts.outputJoinFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + opts.outputJoinFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-l" { - opts.leftJoinFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + opts.leftJoinFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--lk" || opt == "--left-keep-field-names" { - opts.leftKeepFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + opts.leftKeepFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-r" { - opts.rightJoinFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + opts.rightJoinFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--lp" { - opts.leftPrefix = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + opts.leftPrefix, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--rp" { - opts.rightPrefix = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + opts.rightPrefix, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--np" { opts.emitPairables = false @@ -219,8 +247,7 @@ func transformerJoinParseCLI( // Nothing else to handle here. argi = largi } else { - transformerJoinUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "input format option not recognized") } } } @@ -228,25 +255,15 @@ func transformerJoinParseCLI( cli.FinalizeReaderOptions(&opts.joinFlagOptions.ReaderOptions) if opts.leftFileName == "" { - fmt.Fprintf(os.Stderr, "%s %s: need left file name\n", "mlr", verb) - transformerJoinUsage(os.Stderr) - os.Exit(1) - return nil + return nil, cli.VerbErrorf(verb, "need left file name") } if !opts.emitPairables && !opts.emitLeftUnpairables && !opts.emitRightUnpairables { - fmt.Fprintf(os.Stderr, "%s %s: all emit flags are unset; no output is possible.\n", - "mlr", verb) - transformerJoinUsage(os.Stderr) - os.Exit(1) - return nil + return nil, cli.VerbErrorf(verb, "all emit flags are unset; no output is possible") } if opts.outputJoinFieldNames == nil { - fmt.Fprintf(os.Stderr, "%s %s: need output field names\n", "mlr", verb) - transformerJoinUsage(os.Stderr) - os.Exit(1) - return nil + return nil, cli.VerbErrorf(verb, "need output field names") } if opts.leftJoinFieldNames == nil { @@ -260,24 +277,20 @@ func transformerJoinParseCLI( rlen := len(opts.rightJoinFieldNames) olen := len(opts.outputJoinFieldNames) if llen != rlen || llen != olen { - fmt.Fprintf(os.Stderr, - "%s %s: must have equal left,right,output field-name lists; got lengths %d,%d,%d.\n", - "mlr", verb, llen, rlen, olen) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "left, right, and output field lists must have same length") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerJoin(opts) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerJoin struct { @@ -470,9 +483,9 @@ func (tr *TransformerJoin) ingestLeftFile() { // Instantiate the record-reader // TODO: perhaps increase recordsPerBatch, and/or refactor recordReader, err := input.Create(readerOpts, 1) - if recordReader == nil { - fmt.Fprintf(os.Stderr, "mlr join: %v\n", err) - os.Exit(1) + if err != nil { + // TODO: propagate error to caller + return } // Set the initial context for the left-file. @@ -501,8 +514,9 @@ func (tr *TransformerJoin) ingestLeftFile() { select { case err := <-errorChannel: - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + // TODO: propagate error to caller + _ = err + return case leftrecsAndContexts := <-readerChannel: // TODO: temp for batch-reader refactor diff --git a/pkg/transformers/json_parse.go b/pkg/transformers/json_parse.go index ae73b0df2..634b3b332 100644 --- a/pkg/transformers/json_parse.go +++ b/pkg/transformers/json_parse.go @@ -40,7 +40,7 @@ func transformerJSONParseParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -50,6 +50,7 @@ func transformerJSONParseParseCLI( var fieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -62,23 +63,25 @@ func transformerJSONParseParseCLI( if opt == "-h" || opt == "--help" { transformerJSONParseUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-k" { keepFailed = true } else { - transformerJSONParseUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerJSONParse( @@ -86,11 +89,10 @@ func transformerJSONParseParseCLI( keepFailed, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerJSONParse struct { diff --git a/pkg/transformers/json_stringify.go b/pkg/transformers/json_stringify.go index e52b30822..e09ef841b 100644 --- a/pkg/transformers/json_stringify.go +++ b/pkg/transformers/json_stringify.go @@ -40,7 +40,7 @@ func transformerJSONStringifyParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -50,6 +50,7 @@ func transformerJSONStringifyParseCLI( var fieldNames []string = nil jvStack := false // TODO: ?? + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -62,10 +63,13 @@ func transformerJSONStringifyParseCLI( if opt == "-h" || opt == "--help" { transformerJSONStringifyUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--jvstack" { jvStack = true @@ -74,8 +78,7 @@ func transformerJSONStringifyParseCLI( jvStack = false } else { - transformerJSONStringifyUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } @@ -88,7 +91,7 @@ func transformerJSONStringifyParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerJSONStringify( @@ -96,11 +99,10 @@ func transformerJSONStringifyParseCLI( fieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerJSONStringify struct { diff --git a/pkg/transformers/label.go b/pkg/transformers/label.go index 6d0c32137..9a1f35f8a 100644 --- a/pkg/transformers/label.go +++ b/pkg/transformers/label.go @@ -38,7 +38,7 @@ func transformerLabelParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -56,36 +56,32 @@ func transformerLabelParseCLI( if opt == "-h" || opt == "--help" { transformerLabelUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerLabelUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameLabel, "option \"%s\" not recognized", opt) } // Get the label field names from the command line if argi >= argc { - transformerLabelUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameLabel, "field names required") } newNames := lib.SplitString(args[argi], ",") argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerLabel( newNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr label: %v\n", err) - os.Exit(1) - // TODO: return nil to caller and have it exit, maybe + return nil, err } - return transformer + return transformer, nil } type TransformerLabel struct { @@ -100,7 +96,7 @@ func NewTransformerLabel( for _, newName := range newNames { _, ok := uniquenessChecker[newName] if ok { - return nil, fmt.Errorf(`labels must be unique; got duplicate "%s"`, newName) + return nil, fmt.Errorf(`mlr label: labels must be unique; got duplicate "%s"`, newName) } uniquenessChecker[newName] = true } diff --git a/pkg/transformers/latin1_to_utf8.go b/pkg/transformers/latin1_to_utf8.go index 6212c072c..a93acb21d 100644 --- a/pkg/transformers/latin1_to_utf8.go +++ b/pkg/transformers/latin1_to_utf8.go @@ -36,7 +36,7 @@ func transformerLatin1ToUTF8ParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -54,25 +54,23 @@ func transformerLatin1ToUTF8ParseCLI( if opt == "-h" || opt == "--help" { transformerLatin1ToUTF8Usage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerLatin1ToUTF8Usage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameLatin1ToUTF8, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerLatin1ToUTF8() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerLatin1ToUTF8 struct { diff --git a/pkg/transformers/merge_fields.go b/pkg/transformers/merge_fields.go index 6d6c71d6e..a34625d71 100644 --- a/pkg/transformers/merge_fields.go +++ b/pkg/transformers/merge_fields.go @@ -75,7 +75,7 @@ func transformerMergeFieldsParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -89,6 +89,7 @@ func transformerMergeFieldsParseCLI( keepInputFields := false doInterpolatedPercentiles := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -101,25 +102,40 @@ func transformerMergeFieldsParseCLI( if opt == "-h" || opt == "--help" { transformerMergeFieldsUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-a" { - accumulatorNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + accumulatorNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doWhich = e_MERGE_BY_NAME_LIST } else if opt == "-r" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doWhich = e_MERGE_BY_NAME_REGEX } else if opt == "-c" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doWhich = e_MERGE_BY_COLLAPSING } else if opt == "-o" { - outputFieldBasename = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldBasename, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-k" { keepInputFields = true @@ -134,32 +150,26 @@ func transformerMergeFieldsParseCLI( // No-op pass-through for backward compatibility with Miller 5 } else { - transformerMergeFieldsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } // TODO: libify for use across verbs. if len(accumulatorNameList) == 0 { - fmt.Fprintf(os.Stderr, "%s %s: -a option is required.\n", "mlr", verbNameMergeFields) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", "mlr", verbNameMergeFields) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameMergeFields, "-a option is required") } if len(valueFieldNameList) == 0 { - fmt.Fprintf(os.Stderr, "%s %s: -f option is required.\n", "mlr", verbNameMergeFields) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", "mlr", verbNameMergeFields) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameMergeFields, "-f option is required") } if outputFieldBasename == "" { if doWhich == e_MERGE_BY_NAME_LIST || doWhich == e_MERGE_BY_NAME_REGEX { - transformerMergeFieldsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameMergeFields, "-o output field basename required") } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerMergeFields( @@ -171,11 +181,10 @@ func transformerMergeFieldsParseCLI( keepInputFields, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } // Given: accumulate count,sum on values x,y group by a,b. diff --git a/pkg/transformers/most_or_least_frequent.go b/pkg/transformers/most_or_least_frequent.go index a60de2e48..4c5a97933 100644 --- a/pkg/transformers/most_or_least_frequent.go +++ b/pkg/transformers/most_or_least_frequent.go @@ -70,7 +70,7 @@ func transformerMostFrequentParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { return transformerMostOrLeastFrequentParseCLI(pargi, argc, args, true, transformerMostFrequentUsage, doConstruct) } @@ -80,7 +80,7 @@ func transformerLeastFrequentParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { return transformerMostOrLeastFrequentParseCLI(pargi, argc, args, false, transformerLeastFrequentUsage, doConstruct) } @@ -91,7 +91,7 @@ func transformerMostOrLeastFrequentParseCLI( descending bool, usageFunc TransformerUsageFunc, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -104,6 +104,7 @@ func transformerMostOrLeastFrequentParseCLI( showCounts := true outputFieldName := mostLeastFrequentDefaultOutputFieldName + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -116,35 +117,41 @@ func transformerMostOrLeastFrequentParseCLI( if opt == "-h" || opt == "--help" { usageFunc(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-n" { - maxOutputLength = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + maxOutputLength, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-b" { showCounts = false } else if opt == "-o" { - outputFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - usageFunc(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if groupByFieldNames == nil { - usageFunc(os.Stderr) - os.Exit(1) - return nil + return nil, cli.VerbErrorf(verb, "-f field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerMostOrLeastFrequent( @@ -155,11 +162,10 @@ func transformerMostOrLeastFrequentParseCLI( descending, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerMostOrLeastFrequent struct { diff --git a/pkg/transformers/nest.go b/pkg/transformers/nest.go index 60c08733f..98a67ced5 100644 --- a/pkg/transformers/nest.go +++ b/pkg/transformers/nest.go @@ -94,7 +94,7 @@ func transformerNestParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -127,14 +127,22 @@ func transformerNestParseCLI( if opt == "-h" || opt == "--help" { transformerNestUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + fieldName = s } else if opt == "-r" { doRegexes = true - fieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + fieldName = s } else if opt == "--explode" || opt == "-e" { doExplode = true @@ -158,12 +166,24 @@ func transformerNestParseCLI( doAcrossFieldsSpecified = true } else if opt == "--nested-fs" || opt == "-S" { - nestedFS = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + nestedFS = s } else if opt == "--nested-ps" || opt == "-P" { - nestedPS = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + nestedPS = s } else if opt == "--evar" { - evfs = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + evfs = s doExplode = true doExplodeSpecified = true doPairs = false @@ -172,7 +192,11 @@ func transformerNestParseCLI( doAcrossFieldsSpecified = true } else if opt == "--ivar" { - ivfs = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + s, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + ivfs = s doExplode = false doExplodeSpecified = true doPairs = false @@ -182,7 +206,7 @@ func transformerNestParseCLI( } else { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: option \"%s\" not recognized", "mlr", verb, opt) } } @@ -201,32 +225,31 @@ func transformerNestParseCLI( if fieldName == "" { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: -f or -r is required", "mlr", verb) } if !doExplodeSpecified { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: --explode or --implode is required", "mlr", verb) } if !doPairsSpecified { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: --values or --pairs is required", "mlr", verb) } if !doAcrossFieldsSpecified { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: --across-records or --across-fields is required", "mlr", verb) } if doPairs && !doExplode { transformerNestUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: --implode with --pairs doesn't make sense", "mlr", verb) } if doRegexes && !doExplode { - fmt.Fprintf(os.Stderr, "mlr nest: -r is only supported with --explode, not --implode.\n") - os.Exit(1) + return nil, fmt.Errorf("mlr nest: -r is only supported with --explode, not --implode") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerNest( @@ -239,11 +262,10 @@ func transformerNestParseCLI( doAcrossFields, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerNest struct { diff --git a/pkg/transformers/nothing.go b/pkg/transformers/nothing.go index 76199a907..6fb0cfd69 100644 --- a/pkg/transformers/nothing.go +++ b/pkg/transformers/nothing.go @@ -34,7 +34,7 @@ func transformerNothingParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -52,25 +52,23 @@ func transformerNothingParseCLI( if opt == "-h" || opt == "--help" { transformerNothingUsage(os.Stdout) - os.Exit(0) - + return nil, cli.ErrHelpRequested } transformerNothingUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: option \"%s\" not recognized", "mlr", verbNameNothing, opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerNothing() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerNothing struct { diff --git a/pkg/transformers/put_or_filter.go b/pkg/transformers/put_or_filter.go index bda7434ba..5ceab7351 100644 --- a/pkg/transformers/put_or_filter.go +++ b/pkg/transformers/put_or_filter.go @@ -184,7 +184,7 @@ func transformerPutOrFilterParseCLI( args []string, mainOptions *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -217,10 +217,8 @@ func transformerPutOrFilterParseCLI( for _, filename := range options.DSLPreloadFileNames { theseDSLStrings, err := lib.LoadStringsFromFileOrDir(filename, ".mlr") if err != nil { - fmt.Fprintf(os.Stderr, "%s %s: cannot load DSL expression from \"%s\": ", - "mlr", verb, filename) - fmt.Println(err) - os.Exit(1) + return nil, fmt.Errorf("%s %s: cannot load DSL expression from \"%s\": %w", + "mlr", verb, filename, err) } dslStrings = append(dslStrings, theseDSLStrings...) } @@ -238,11 +236,14 @@ func transformerPutOrFilterParseCLI( if opt == "-h" || opt == "--help" { transformerPutOrFilterUsage(os.Stdout, verb) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { // Get a DSL string from the user-specified filename - filename := cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + filename, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } // Miller has a two-pass command-line parser. If the user does // `mlr put -f foo.mlr` @@ -258,17 +259,18 @@ func transformerPutOrFilterParseCLI( if doConstruct { theseDSLStrings, err := lib.LoadStringsFromFileOrDir(filename, ".mlr") if err != nil { - fmt.Fprintf(os.Stderr, "%s %s: cannot load DSL expression from file \"%s\": ", - "mlr", verb, filename) - fmt.Println(err) - os.Exit(1) + return nil, fmt.Errorf("%s %s: cannot load DSL expression from file \"%s\": %w", + "mlr", verb, filename, err) } dslStrings = append(dslStrings, theseDSLStrings...) } haveDSLStringsHere = true } else if opt == "-e" { - dslString := cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + dslString, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } dslStrings = append(dslStrings, dslString) haveDSLStringsHere = true @@ -277,7 +279,10 @@ func transformerPutOrFilterParseCLI( // mlr put -s sum=0 // is like // mlr put -s 'begin {@sum = 0}' - preset := cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + preset, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } presets = append(presets, preset) } else if opt == "-x" { @@ -328,8 +333,7 @@ func transformerPutOrFilterParseCLI( // Nothing else to handle here. argi = largi } else { - transformerPutOrFilterUsage(os.Stderr, verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option not recognized") } } } @@ -342,8 +346,7 @@ func transformerPutOrFilterParseCLI( if !haveDSLStringsHere { // Get the DSL string from the command line, after the flags if argi >= argc { - fmt.Fprintf(os.Stderr, "mlr %s: -f/-e requires a filename as argument.\n", verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "expression or -f/-e is required") } dslString := args[argi] dslStrings = append(dslStrings, dslString) @@ -352,7 +355,7 @@ func transformerPutOrFilterParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } var dslInstanceType cst.DSLInstanceType = cst.DSLInstanceTypePut @@ -380,11 +383,10 @@ func transformerPutOrFilterParseCLI( options, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerPut struct { diff --git a/pkg/transformers/regularize.go b/pkg/transformers/regularize.go index 54342ac00..c862967fa 100644 --- a/pkg/transformers/regularize.go +++ b/pkg/transformers/regularize.go @@ -35,7 +35,7 @@ func transformerRegularizeParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -53,25 +53,23 @@ func transformerRegularizeParseCLI( if opt == "-h" || opt == "--help" { transformerRegularizeUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerRegularizeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameRegularize, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerRegularize() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerRegularize struct { diff --git a/pkg/transformers/remove_empty_columns.go b/pkg/transformers/remove_empty_columns.go index ef7558b6d..e939ecfca 100644 --- a/pkg/transformers/remove_empty_columns.go +++ b/pkg/transformers/remove_empty_columns.go @@ -34,7 +34,7 @@ func transformerRemoveEmptyColumnsParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -52,25 +52,23 @@ func transformerRemoveEmptyColumnsParseCLI( if opt == "-h" || opt == "--help" { transformerRemoveEmptyColumnsUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerRemoveEmptyColumnsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameRemoveEmptyColumns, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerRemoveEmptyColumns() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerRemoveEmptyColumns struct { diff --git a/pkg/transformers/rename.go b/pkg/transformers/rename.go index 282f6e8fa..7da5fcb6e 100644 --- a/pkg/transformers/rename.go +++ b/pkg/transformers/rename.go @@ -55,7 +55,7 @@ func transformerRenameParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -76,7 +76,7 @@ func transformerRenameParseCLI( if opt == "-h" || opt == "--help" { transformerRenameUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-r" { doRegexes = true @@ -85,8 +85,7 @@ func transformerRenameParseCLI( doGsub = true } else { - transformerRenameUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameRename, "option \"%s\" not recognized", opt) } } @@ -96,19 +95,17 @@ func transformerRenameParseCLI( // Get the rename field names from the command line if argi >= argc { - transformerRenameUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameRename, "old1,new1,old2,new2,... required") } names := lib.SplitString(args[argi], ",") if len(names)%2 != 0 { - transformerRenameUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameRename, "names string must have even length") } argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerRename( @@ -117,11 +114,10 @@ func transformerRenameParseCLI( doGsub, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type tRegexAndReplacement struct { diff --git a/pkg/transformers/reorder.go b/pkg/transformers/reorder.go index 1e8ce875d..3f6875111 100644 --- a/pkg/transformers/reorder.go +++ b/pkg/transformers/reorder.go @@ -56,7 +56,7 @@ func transformerReorderParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -68,6 +68,7 @@ func transformerReorderParseCLI( putAfter := false centerFieldName := "" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -80,22 +81,34 @@ func transformerReorderParseCLI( if opt == "-h" || opt == "--help" { transformerReorderUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexes = false } else if opt == "-r" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexes = true } else if opt == "-b" { - centerFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + centerFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } putAfter = false } else if opt == "-a" { - centerFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + centerFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } putAfter = true } else if opt == "-e" { @@ -103,19 +116,17 @@ func transformerReorderParseCLI( centerFieldName = "" } else { - transformerReorderUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fieldNames == nil { - transformerReorderUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerReorder( @@ -125,11 +136,10 @@ func transformerReorderParseCLI( centerFieldName, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerReorder struct { diff --git a/pkg/transformers/repeat.go b/pkg/transformers/repeat.go index 5e5b5c751..590d7d620 100644 --- a/pkg/transformers/repeat.go +++ b/pkg/transformers/repeat.go @@ -63,7 +63,7 @@ func transformerRepeatParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { repeatCountSource := repeatCountSourceUnspecified repeatCount := int64(0) @@ -74,6 +74,7 @@ func transformerRepeatParseCLI( verb := args[argi] argi++ + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -86,30 +87,34 @@ func transformerRepeatParseCLI( if opt == "-h" || opt == "--help" { transformerRepeatUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - repeatCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + repeatCount, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } repeatCountSource = repeatCountFromInt } else if opt == "-f" { - repeatCountFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + repeatCountFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } repeatCountSource = repeatCountFromFieldName } else { - transformerRepeatUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if repeatCountSource == repeatCountSourceUnspecified { - transformerRepeatUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-n or -f is required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerRepeat( @@ -118,11 +123,10 @@ func transformerRepeatParseCLI( repeatCountFieldName, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerRepeat struct { diff --git a/pkg/transformers/reshape.go b/pkg/transformers/reshape.go index 12a3986c1..e72657599 100644 --- a/pkg/transformers/reshape.go +++ b/pkg/transformers/reshape.go @@ -120,7 +120,7 @@ func transformerReshapeParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -133,6 +133,7 @@ func transformerReshapeParseCLI( var outputFieldNames []string = nil var splitOutFieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -145,24 +146,35 @@ func transformerReshapeParseCLI( if opt == "-h" || opt == "--help" { transformerReshapeUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-i" { - inputFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + inputFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-r" { - inputFieldRegexString := cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + inputFieldRegexString, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } if inputFieldRegexStrings == nil { inputFieldRegexStrings = []string{} } inputFieldRegexStrings = append(inputFieldRegexStrings, inputFieldRegexString) } else if opt == "-o" { - outputFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + outputFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-s" { - splitOutFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + splitOutFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerReshapeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } @@ -174,25 +186,21 @@ func transformerReshapeParseCLI( if splitOutFieldNames == nil { // wide to long if inputFieldNames == nil && inputFieldRegexStrings == nil { - transformerReshapeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-i or -r is required for wide-to-long") } if outputFieldNames == nil { - transformerReshapeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-o is required for wide-to-long") } if len(outputFieldNames) != 2 { - transformerReshapeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-o must have exactly 2 field names for wide-to-long") } outputKeyFieldName = outputFieldNames[0] outputValueFieldName = outputFieldNames[1] } else { // long to wide if len(splitOutFieldNames) != 2 { - transformerReshapeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-s must have exactly 2 field names for long-to-wide") } splitOutKeyFieldName = splitOutFieldNames[0] splitOutValueFieldName = splitOutFieldNames[1] @@ -200,7 +208,7 @@ func transformerReshapeParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerReshape( @@ -212,11 +220,10 @@ func transformerReshapeParseCLI( splitOutValueFieldName, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerReshape struct { diff --git a/pkg/transformers/sample.go b/pkg/transformers/sample.go index 8f8c71683..9781f2067 100644 --- a/pkg/transformers/sample.go +++ b/pkg/transformers/sample.go @@ -39,7 +39,7 @@ func transformerSampleParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -49,6 +49,7 @@ func transformerSampleParseCLI( sampleCount := int64(-1) var groupByFieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -61,28 +62,32 @@ func transformerSampleParseCLI( if opt == "-h" || opt == "--help" { transformerSampleUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-k" { - sampleCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + sampleCount, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerSampleUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if sampleCount < 0 { - transformerSampleUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "sample count must be non-negative") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSample( @@ -90,11 +95,10 @@ func transformerSampleParseCLI( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type sampleBucketType struct { diff --git a/pkg/transformers/sec2gmt.go b/pkg/transformers/sec2gmt.go index 33b64b978..7881363ce 100644 --- a/pkg/transformers/sec2gmt.go +++ b/pkg/transformers/sec2gmt.go @@ -43,7 +43,7 @@ func transformerSec2GMTParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -64,7 +64,7 @@ func transformerSec2GMTParseCLI( if opt == "-h" || opt == "--help" { transformerSec2GMTUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-1" { numDecimalPlaces = 1 @@ -93,21 +93,19 @@ func transformerSec2GMTParseCLI( preDivide = 1.0e9 } else { - transformerSec2GMTUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSec2GMT, "option \"%s\" not recognized", opt) } } if argi >= argc { - transformerSec2GMTUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSec2GMT, "field names required") } fieldNames := args[argi] argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSec2GMT( @@ -116,11 +114,10 @@ func transformerSec2GMTParseCLI( numDecimalPlaces, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSec2GMT struct { diff --git a/pkg/transformers/sec2gmtdate.go b/pkg/transformers/sec2gmtdate.go index 5bad990b3..296b90a65 100644 --- a/pkg/transformers/sec2gmtdate.go +++ b/pkg/transformers/sec2gmtdate.go @@ -37,7 +37,7 @@ func transformerSec2GMTDateParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -55,34 +55,31 @@ func transformerSec2GMTDateParseCLI( if opt == "-h" || opt == "--help" { transformerSec2GMTDateUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerSec2GMTDateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSec2GMTDate, "option \"%s\" not recognized", opt) } if argi >= argc { - transformerSec2GMTDateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSec2GMTDate, "field names required") } fieldNames := args[argi] argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSec2GMTDate( fieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSec2GMTDate struct { diff --git a/pkg/transformers/seqgen.go b/pkg/transformers/seqgen.go index d183ad057..06e6f3d11 100644 --- a/pkg/transformers/seqgen.go +++ b/pkg/transformers/seqgen.go @@ -46,7 +46,7 @@ func transformerSeqgenParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -58,6 +58,7 @@ func transformerSeqgenParseCLI( stopString := "100" stepString := "1" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -70,29 +71,40 @@ func transformerSeqgenParseCLI( if opt == "-h" || opt == "--help" { transformerSeqgenUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--start" { - startString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + startString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--stop" { - stopString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + stopString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--step" { - stepString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + stepString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerSeqgenUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSeqgen( @@ -102,11 +114,10 @@ func transformerSeqgenParseCLI( stepString, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSeqgen struct { diff --git a/pkg/transformers/shuffle.go b/pkg/transformers/shuffle.go index b7874cd02..1583f4937 100644 --- a/pkg/transformers/shuffle.go +++ b/pkg/transformers/shuffle.go @@ -37,7 +37,7 @@ func transformerShuffleParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -55,25 +55,23 @@ func transformerShuffleParseCLI( if opt == "-h" || opt == "--help" { transformerShuffleUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerShuffleUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameShuffle, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerShuffle() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerShuffle struct { diff --git a/pkg/transformers/skip_trivial_records.go b/pkg/transformers/skip_trivial_records.go index 3cb5b131c..de4a456d4 100644 --- a/pkg/transformers/skip_trivial_records.go +++ b/pkg/transformers/skip_trivial_records.go @@ -34,7 +34,7 @@ func transformerSkipTrivialRecordsParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -52,25 +52,23 @@ func transformerSkipTrivialRecordsParseCLI( if opt == "-h" || opt == "--help" { transformerSkipTrivialRecordsUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerSkipTrivialRecordsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSkipTrivialRecords, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSkipTrivialRecords() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSkipTrivialRecords struct { diff --git a/pkg/transformers/sort.go b/pkg/transformers/sort.go index b8d576838..e5a965da8 100644 --- a/pkg/transformers/sort.go +++ b/pkg/transformers/sort.go @@ -95,7 +95,7 @@ func transformerSortParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -118,10 +118,13 @@ func transformerSortParseCLI( if opt == "-h" || opt == "--help" { transformerSortUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.LexicalAscendingComparator) @@ -131,16 +134,24 @@ func transformerSortParseCLI( // See comments over "-n" -- similar hack. if args[argi] == "-r" { // Treat like "-cr" - cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1); err != nil { + return nil, err + } argi++ - subList := cli.VerbGetStringArrayArgOrDie(verb, "-nr", args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, "-nr", args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.CaseFoldDescendingComparator) } } else { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.CaseFoldAscendingComparator) @@ -149,18 +160,28 @@ func transformerSortParseCLI( } else if opt == "-t" { // See comments over "-n" -- similar hack. - cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1); err != nil { + return nil, err + } if args[argi] == "-r" { // Treat like "-tr" - cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1); err != nil { + return nil, err + } argi++ - subList := cli.VerbGetStringArrayArgOrDie(verb, "-tr", args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, "-tr", args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NaturalAscendingComparator) } } else { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NaturalDescendingComparator) @@ -169,18 +190,28 @@ func transformerSortParseCLI( } else if opt == "-r" { // See comments over "-n" -- similar hack. - cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1); err != nil { + return nil, err + } if args[argi] == "-t" { // Treat like "-rt" which is same as "-tr" - cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, args[argi], args, argi, argc, 1); err != nil { + return nil, err + } argi++ - subList := cli.VerbGetStringArrayArgOrDie(verb, "-tr", args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, "-tr", args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NaturalAscendingComparator) } } else { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.LexicalDescendingComparator) @@ -210,12 +241,17 @@ func transformerSortParseCLI( // So here we special-case this: if "-n" is followed immediately by // "-f", we treat it the same as "-nf". Likewise, "-n" followed by // "-r" is treated like "-nr". - cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1) + if err := cli.VerbCheckArgCount(verb, opt, args, argi, argc, 1); err != nil { + return nil, err + } if args[argi] == "-f" { // Treat like "-nf" argi++ - subList := cli.VerbGetStringArrayArgOrDie(verb, "-nf", args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, "-nf", args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NumericAscendingComparator) @@ -224,7 +260,10 @@ func transformerSortParseCLI( } else if args[argi] == "-r" { // Treat like "-nr" argi++ - subList := cli.VerbGetStringArrayArgOrDie(verb, "-nr", args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, "-nr", args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NumericDescendingComparator) @@ -232,7 +271,10 @@ func transformerSortParseCLI( } else { // Treat like "-n" - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NumericAscendingComparator) @@ -240,14 +282,20 @@ func transformerSortParseCLI( } } else if opt == "-nf" { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NumericAscendingComparator) } } else if opt == "-nr" { - subList := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + subList, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, item := range subList { groupByFieldNames = append(groupByFieldNames, item) comparatorFuncs = append(comparatorFuncs, mlrval.NumericDescendingComparator) @@ -257,19 +305,17 @@ func transformerSortParseCLI( doMoveToHead = true } else { - transformerSortUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if len(groupByFieldNames) == 0 { - transformerSortUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f sort fields required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSort( @@ -278,11 +324,10 @@ func transformerSortParseCLI( doMoveToHead, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } // Example: diff --git a/pkg/transformers/sort_within_records.go b/pkg/transformers/sort_within_records.go index 696490ca9..d9a27ddfe 100644 --- a/pkg/transformers/sort_within_records.go +++ b/pkg/transformers/sort_within_records.go @@ -34,7 +34,7 @@ func transformerSortWithinRecordsParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -53,14 +53,13 @@ func transformerSortWithinRecordsParseCLI( if opt == "-h" || opt == "--help" { transformerSortWithinRecordsUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-r" { doRecurse = true } else { - transformerSortWithinRecordsUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameSortWithinRecords, "option \"%s\" not recognized", opt) } } @@ -69,16 +68,15 @@ func transformerSortWithinRecordsParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSortWithinRecords(doRecurse) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSortWithinRecords struct { diff --git a/pkg/transformers/sparsify.go b/pkg/transformers/sparsify.go index 11ab18b81..b365b4aa6 100644 --- a/pkg/transformers/sparsify.go +++ b/pkg/transformers/sparsify.go @@ -46,7 +46,7 @@ func transformerSparsifyParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -56,6 +56,7 @@ func transformerSparsifyParseCLI( fillerString := "" var specifiedFieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -68,23 +69,28 @@ func transformerSparsifyParseCLI( if opt == "-h" || opt == "--help" { transformerSparsifyUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-s" { - fillerString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fillerString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - specifiedFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + specifiedFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerSparsifyUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSparsify( @@ -92,11 +98,10 @@ func transformerSparsifyParseCLI( specifiedFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSparsify struct { diff --git a/pkg/transformers/split.go b/pkg/transformers/split.go index 5224782b3..fe7be7a82 100644 --- a/pkg/transformers/split.go +++ b/pkg/transformers/split.go @@ -80,7 +80,7 @@ func transformerSplitParseCLI( args []string, mainOptions *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -107,6 +107,7 @@ func transformerSplitParseCLI( } // Parse local flags. + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -119,28 +120,46 @@ func transformerSplitParseCLI( if opt == "-h" || opt == "--help" { transformerSplitUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - n = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + n, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doSize = true } else if opt == "-m" { - n = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + n, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doMod = true } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--prefix" { - outputFileNamePrefix = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFileNamePrefix, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--suffix" { - outputFileNameSuffix = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFileNameSuffix, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } haveOutputFileNameSuffix = true } else if opt == "--folder" { - outputFolder = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFolder, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-a" { doAppend = true @@ -152,7 +171,10 @@ func transformerSplitParseCLI( escapeFileNameCharacters = false } else if opt == "-j" { - fileNamePartJoiner = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fileNamePartJoiner, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { // This is inelegant. For error-proofing we advance argi already in our @@ -164,20 +186,17 @@ func transformerSplitParseCLI( // Nothing else to handle here. argi = largi } else { - transformerSplitUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "output format not recognized") } } } doGroup := groupByFieldNames != nil if !doMod && !doSize && !doGroup { - fmt.Fprintf(os.Stderr, "mlr %s: At least one of -m, -n, or -g is required.\n", verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-n, -g, or -s is required") } if (doMod && doSize) || (doMod && doGroup) || (doSize && doGroup) { - fmt.Fprintf(os.Stderr, "mlr %s: Only one of -m, -n, or -g is required.\n", verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-n, -g, and -s are mutually exclusive") } cli.FinalizeWriterOptions(&localOptions.WriterOptions) @@ -187,7 +206,7 @@ func transformerSplitParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSplit( @@ -209,7 +228,7 @@ func transformerSplitParseCLI( os.Exit(1) } - return transformer + return transformer, nil } type TransformerSplit struct { @@ -310,7 +329,7 @@ func (tr *TransformerSplit) splitModUngrouped( err := tr.outputHandlerManager.WriteRecordAndContext(inrecAndContext, filename) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: file-write error: %v\n", err) + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } @@ -346,7 +365,7 @@ func (tr *TransformerSplit) splitSizeUngrouped( if tr.outputHandler != nil { err = tr.outputHandler.Close() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: file-close error: %v\n", err) + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } } @@ -358,7 +377,7 @@ func (tr *TransformerSplit) splitSizeUngrouped( tr.doAppend, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: file-open error: %v\n", err) + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } @@ -367,7 +386,7 @@ func (tr *TransformerSplit) splitSizeUngrouped( err = tr.outputHandler.WriteRecordAndContext(inrecAndContext) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: file-write error: %v\n", err) + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } @@ -383,7 +402,7 @@ func (tr *TransformerSplit) splitSizeUngrouped( if tr.outputHandler != nil { err := tr.outputHandler.Close() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: file-close error: %v\n", err) + fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } } diff --git a/pkg/transformers/stats1.go b/pkg/transformers/stats1.go index fe4ba1d81..5563776a9 100644 --- a/pkg/transformers/stats1.go +++ b/pkg/transformers/stats1.go @@ -92,7 +92,7 @@ func transformerStats1ParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -111,6 +111,7 @@ func transformerStats1ParseCLI( doInterpolatedPercentiles := false doIterativeStats := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -123,30 +124,51 @@ func transformerStats1ParseCLI( if opt == "-h" || opt == "--help" { transformerStats1Usage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-a" { - accumulatorNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + accumulatorNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "--fr" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexValueFieldNames = true } else if opt == "--fx" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexValueFieldNames = true invertRegexValueFieldNames = true } else if opt == "--gr" { - groupByFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexGroupByFieldNames = true } else if opt == "--gx" { - groupByFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doRegexGroupByFieldNames = true invertRegexGroupByFieldNames = true @@ -154,7 +176,10 @@ func transformerStats1ParseCLI( doRegexValueFieldNames = true doRegexGroupByFieldNames = true invertRegexValueFieldNames = true - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } groupByFieldNameList = lib.CopyStringArray(valueFieldNameList) } else if opt == "-i" { @@ -170,26 +195,21 @@ func transformerStats1ParseCLI( // No-op pass-through for backward compatibility with Miller 5 } else { - transformerStats1Usage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameStats1, "option \"%s\" not recognized", opt) } } // TODO: libify for use across verbs. if len(accumulatorNameList) == 0 { - fmt.Fprintf(os.Stderr, "%s %s: -a option is required.\n", "mlr", verbNameStats1) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", "mlr", verbNameStats1) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameStats1, "-a option is required") } if len(valueFieldNameList) == 0 { - fmt.Fprintf(os.Stderr, "%s %s: -f option is required.\n", "mlr", verbNameStats1) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", "mlr", verbNameStats1) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameStats1, "-f option is required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerStats1( @@ -206,11 +226,10 @@ func transformerStats1ParseCLI( doIterativeStats, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr stats1: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerStats1 struct { @@ -309,7 +328,7 @@ func NewTransformerStats1( ) (*TransformerStats1, error) { for _, name := range accumulatorNameList { if !utils.ValidateStats1AccumulatorName(name) { - return nil, fmt.Errorf(`accumulator "%s" not found`, name) + return nil, fmt.Errorf(`mlr stats1: accumulator "%s" not found`, name) } } diff --git a/pkg/transformers/stats2.go b/pkg/transformers/stats2.go index 6ed329b35..fbef064c2 100644 --- a/pkg/transformers/stats2.go +++ b/pkg/transformers/stats2.go @@ -62,15 +62,13 @@ func transformerStats2ParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi verb := args[argi] argi++ - argv0 := "mlr" - var accumulatorNameList []string = nil var valueFieldNameList []string = nil groupByFieldNameList := []string{} @@ -78,6 +76,7 @@ func transformerStats2ParseCLI( doIterativeStats := false doHoldAndFit := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -90,16 +89,25 @@ func transformerStats2ParseCLI( if opt == "-h" || opt == "--help" { transformerStats2Usage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-a" { - accumulatorNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + accumulatorNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - valueFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNameList = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNameList, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-v" { doVerbose = true @@ -120,34 +128,26 @@ func transformerStats2ParseCLI( // for all applicable stats2 accumulators (i.e. none of them). } else { - transformerStats2Usage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if doIterativeStats && doHoldAndFit { - transformerStats2Usage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "cannot combine -I and -H") } if accumulatorNameList == nil { - fmt.Fprintf(os.Stderr, "%s %s: -a option is required.\n", argv0, verb) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", argv0, verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-a option is required") } if valueFieldNameList == nil { - fmt.Fprintf(os.Stderr, "%s %s: -f option is required.\n", argv0, verb) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", argv0, verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f option is required") } if len(valueFieldNameList)%2 != 0 { - fmt.Fprintf(os.Stderr, "%s %s: argument to -f must have even number of fields.\n", argv0, verb) - fmt.Fprintf(os.Stderr, "Please see %s %s --help for more information.\n", argv0, verb) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "argument to -f must have even number of fields") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerStats2( @@ -159,11 +159,10 @@ func transformerStats2ParseCLI( doHoldAndFit, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerStats2 struct { @@ -348,9 +347,7 @@ func (tr *TransformerStats2) ingest( tr.doVerbose, ) if accumulator == nil { - fmt.Fprintf(os.Stderr, "%s %s: accumulator \"%s\" not found.\n", - "mlr", verbNameStats2, accumulatorName, - ) + fmt.Fprintf(os.Stderr, "mlr stats2: accumulator creation failed\n") os.Exit(1) } valueFieldsToAccumulator.Put(accumulatorName, accumulator) diff --git a/pkg/transformers/step.go b/pkg/transformers/step.go index 2b64c4940..302ca4ecd 100644 --- a/pkg/transformers/step.go +++ b/pkg/transformers/step.go @@ -143,7 +143,7 @@ func transformerStepParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -168,51 +168,67 @@ func transformerStepParseCLI( if opt == "-h" || opt == "--help" { transformerStepUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-a" { // Let them do '-a delta -a rsum' or '-a delta,rsum' - stepperNames := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + stepperNames, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, stepperName := range stepperNames { stepperInput := stepperInputFromName(stepperName) if stepperInput == nil { - fmt.Fprintf(os.Stderr, "mlr %s: stepper \"%s\" not found.\n", - verbNameStep, stepperName) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "stepper \"%s\" not found", stepperName) } stepperInputs = append(stepperInputs, stepperInput) } } else if opt == "-f" { // Let them do '-f x -f y' or '-f x,y' - valueFieldNames = append(valueFieldNames, cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)...) + arr, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + valueFieldNames = append(valueFieldNames, arr...) } else if opt == "-g" { // Let them do '-g a -g b' or '-g a,b' - groupByFieldNames = append(groupByFieldNames, cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)...) + arr, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + groupByFieldNames = append(groupByFieldNames, arr...) } else if opt == "-d" { // Let them do '-d 0.8 -d 0.9' or '-d 0.8,0.9' - stringAlphas = append(stringAlphas, cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)...) + arr, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + stringAlphas = append(stringAlphas, arr...) } else if opt == "-o" { // Let them do '-o fast -o slow' or '-o fast,slow' - ewmaSuffixes = append(ewmaSuffixes, cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)...) + arr, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + ewmaSuffixes = append(ewmaSuffixes, arr...) } else if opt == "-F" { // As of Miller 6 this happens automatically, but the flag is accepted // as a no-op for backward compatibility with Miller 5 and below. } else { - transformerStepUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerStep( @@ -223,11 +239,10 @@ func transformerStepParseCLI( ewmaSuffixes, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } // This is the "stepper log" referred to in comments at the top of this file. @@ -436,8 +451,7 @@ func (tr *TransformerStep) handleRecord( tr.ewmaSuffixes, ) if stepper == nil { - fmt.Fprintf(os.Stderr, "mlr %s: stepper \"%s\" not found.\n", - verbNameStep, stepperInput.name) + fmt.Fprintf(os.Stderr, "mlr step: stepper allocation failed\n") os.Exit(1) } accFieldToAccState[stepperInput.name] = stepper diff --git a/pkg/transformers/subs.go b/pkg/transformers/subs.go index 849c0cb46..ca89466a8 100644 --- a/pkg/transformers/subs.go +++ b/pkg/transformers/subs.go @@ -97,7 +97,7 @@ func transformerSubParseCLI( args []string, opts *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSubUsage, NewTransformerSub) } @@ -107,7 +107,7 @@ func transformerGsubParseCLI( args []string, opts *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerGsubUsage, NewTransformerGsub) } @@ -117,7 +117,7 @@ func transformerSsubParseCLI( args []string, opts *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSsubUsage, NewTransformerSsub) } @@ -130,7 +130,7 @@ func transformerSubsParseCLI( doConstruct bool, // false for first pass of CLI-parse, true for second pass usageFunc TransformerUsageFunc, constructorFunc subConstructorFunc, -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -144,6 +144,7 @@ func transformerSubsParseCLI( var oldText string var newText string + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -167,7 +168,10 @@ func transformerSubsParseCLI( doRegexes = true } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } doAllFieldNames = false } else { usageFunc(os.Stderr) @@ -192,7 +196,7 @@ func transformerSubsParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := constructorFunc( @@ -203,11 +207,10 @@ func transformerSubsParseCLI( newText, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerSubs struct { @@ -273,8 +276,7 @@ func NewTransformerSubs( // Handles "a.*b"i Miller case-insensitive-regex specification regex, err := lib.CompileMillerRegex(regexString) if err != nil { - fmt.Fprintf(os.Stderr, "%s %s: cannot compile regex [%s]\n", "mlr", verbNameCut, regexString) - os.Exit(1) + return nil, cli.VerbErrorf("sub", "invalid regex \"%s\": %w", regexString, err) } tr.regexes[i] = regex } diff --git a/pkg/transformers/summary.go b/pkg/transformers/summary.go index 01cd6d2f0..f7a01ab2d 100644 --- a/pkg/transformers/summary.go +++ b/pkg/transformers/summary.go @@ -114,7 +114,7 @@ func transformerSummaryParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -135,6 +135,7 @@ func transformerSummaryParseCLI( transposeOutput := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -147,30 +148,30 @@ func transformerSummaryParseCLI( if opt == "-h" || opt == "--help" { transformerSummaryUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "--all" { summarizerNames = allSummarizerNamesList } else if opt == "-a" { - summarizerNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + summarizerNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, summarizerName := range summarizerNames { if !allSummarizerNamesSet[summarizerName] { - fmt.Fprintf(os.Stderr, "mlr %s: unrecognized summarizer name %s\n", - verb, summarizerName, - ) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "summarizer \"%s\" not found", summarizerName) } } } else if opt == "-x" { - excludeSummarizerNames := cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + excludeSummarizerNames, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } for _, excludeSummarizerName := range excludeSummarizerNames { if !allSummarizerNamesSet[excludeSummarizerName] { - fmt.Fprintf(os.Stderr, "mlr %s: unrecognized Summarizer name %s\n", - verb, excludeSummarizerName, - ) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "exclude summarizer \"%s\" not found", excludeSummarizerName) } } @@ -190,23 +191,21 @@ func transformerSummaryParseCLI( transposeOutput = true } else { - transformerSummaryUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerSummary(summarizerNames, transposeOutput) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type tFieldSummary struct { diff --git a/pkg/transformers/surv.go b/pkg/transformers/surv.go index ae7c9a12a..bc7cba22a 100644 --- a/pkg/transformers/surv.go +++ b/pkg/transformers/surv.go @@ -39,7 +39,7 @@ func transformerSurvParseCLI( args []string, _ *cli.TOptions, doConstruct bool, -) RecordTransformer { +) (RecordTransformer, error) { argi := *pargi verb := args[argi] argi++ @@ -53,19 +53,17 @@ func transformerSurvParseCLI( } if opt == "-h" || opt == "--help" { transformerSurvUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-d" { if argi+1 >= argc { - fmt.Fprintf(os.Stderr, "mlr %s: %s requires an argument\n", verb, opt) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-d requires an argument") } argi++ durationField = args[argi] argi++ } else if opt == "-s" { if argi+1 >= argc { - fmt.Fprintf(os.Stderr, "mlr %s: %s requires an argument\n", verb, opt) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-s requires an argument") } argi++ statusField = args[argi] @@ -76,17 +74,13 @@ func transformerSurvParseCLI( } *pargi = argi if !doConstruct { - return nil + return nil, nil } if durationField == "" { - fmt.Fprintf(os.Stderr, "mlr %s: -d option is required.\n", verbNameSurv) - fmt.Fprintf(os.Stderr, "Please see 'mlr %s --help' for more information.\n", verbNameSurv) - os.Exit(1) + return nil, fmt.Errorf("mlr %s: -d option is required", verb) } if statusField == "" { - fmt.Fprintf(os.Stderr, "mlr %s: -s option is required.\n", verbNameSurv) - fmt.Fprintf(os.Stderr, "Please see 'mlr %s --help' for more information.\n", verbNameSurv) - os.Exit(1) + return nil, fmt.Errorf("mlr %s: -s option is required", verb) } return NewTransformerSurv(durationField, statusField) } @@ -100,13 +94,13 @@ type TransformerSurv struct { } // NewTransformerSurv constructs a new surv transformer. -func NewTransformerSurv(durationField, statusField string) RecordTransformer { +func NewTransformerSurv(durationField, statusField string) (*TransformerSurv, error) { return &TransformerSurv{ durationField: durationField, statusField: statusField, times: []float64{}, events: []bool{}, - } + }, nil } // Transform processes each record or emits results at end-of-stream. @@ -121,14 +115,14 @@ func (tr *TransformerSurv) Transform( rec := inrecAndContext.Record mvDur := rec.Get(tr.durationField) if mvDur == nil { - fmt.Fprintf(os.Stderr, "mlr surv: duration field '%s' not found\n", tr.durationField) - os.Exit(1) + // Skip records missing the duration field + return } duration := mvDur.GetNumericToFloatValueOrDie() mvStat := rec.Get(tr.statusField) if mvStat == nil { - fmt.Fprintf(os.Stderr, "mlr surv: status field '%s' not found\n", tr.statusField) - os.Exit(1) + // Skip records missing the status field + return } status := mvStat.GetNumericToFloatValueOrDie() != 0 tr.times = append(tr.times, duration) diff --git a/pkg/transformers/tac.go b/pkg/transformers/tac.go index b166ed56b..d7017aac3 100644 --- a/pkg/transformers/tac.go +++ b/pkg/transformers/tac.go @@ -33,7 +33,7 @@ func transformerTacParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -51,25 +51,23 @@ func transformerTacParseCLI( if opt == "-h" || opt == "--help" { transformerTacUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerTacUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameTac, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerTac() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerTac struct { diff --git a/pkg/transformers/tail.go b/pkg/transformers/tail.go index c35048ff7..e07e45a2e 100644 --- a/pkg/transformers/tail.go +++ b/pkg/transformers/tail.go @@ -37,7 +37,7 @@ func transformerTailParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -59,10 +59,14 @@ func transformerTailParseCLI( if opt == "-h" || opt == "--help" { transformerTailUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - tailCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + n, err := cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + tailCount = n // This is a bit of a hack. In our Getoptify routine we preprocess // the command line sending '-xyz' to '-x -y -z', but leaving @@ -75,17 +79,21 @@ func transformerTailParseCLI( } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + names, err := cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } + groupByFieldNames = names } else { transformerTailUsage(os.Stderr) - os.Exit(1) + return nil, fmt.Errorf("%s %s: option \"%s\" not recognized", "mlr", verb, opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerTail( @@ -93,11 +101,10 @@ func transformerTailParseCLI( groupByFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerTail struct { diff --git a/pkg/transformers/tee.go b/pkg/transformers/tee.go index eb794e39c..e76acba49 100644 --- a/pkg/transformers/tee.go +++ b/pkg/transformers/tee.go @@ -42,7 +42,7 @@ func transformerTeeParseCLI( args []string, mainOptions *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -72,7 +72,7 @@ func transformerTeeParseCLI( if opt == "-h" || opt == "--help" { transformerTeeUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-a" { appending = true @@ -92,7 +92,6 @@ func transformerTeeParseCLI( // Nothing else to handle here. argi = largi } else { - transformerTeeUsage(os.Stderr) os.Exit(1) } } @@ -102,15 +101,14 @@ func transformerTeeParseCLI( // Get the filename/command from the command line, after the flags if argi >= argc { - transformerTeeUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameTee, "filename or command required") } filenameOrCommand = args[argi] argi++ *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerTee( @@ -124,7 +122,7 @@ func transformerTeeParseCLI( os.Exit(1) } - return transformer + return transformer, nil } type TransformerTee struct { @@ -195,7 +193,6 @@ func (tr *TransformerTee) Transform( "%s: error writing to tee \"%s\":\n", "mlr", tr.filenameOrCommandForDisplay, ) - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } @@ -208,7 +205,6 @@ func (tr *TransformerTee) Transform( "%s: error closing tee \"%s\":\n", "mlr", tr.filenameOrCommandForDisplay, ) - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) os.Exit(1) } *outputRecordsAndContexts = append(*outputRecordsAndContexts, inrecAndContext) diff --git a/pkg/transformers/template.go b/pkg/transformers/template.go index 2ccbad3bf..38546bc52 100644 --- a/pkg/transformers/template.go +++ b/pkg/transformers/template.go @@ -44,7 +44,7 @@ func transformerTemplateParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -54,6 +54,7 @@ func transformerTemplateParseCLI( var fieldNames []string = nil fillWith := "" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -66,37 +67,43 @@ func transformerTemplateParseCLI( if opt == "-h" || opt == "--help" { transformerTemplateUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-t" { - templateFileName := cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + templateFileName, err := cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } temp, err := lib.ReadCSVHeader(templateFileName) if err != nil { - fmt.Println(err) - os.Exit(1) + return nil, fmt.Errorf("mlr %s: cannot read template file: %w", verb, err) } fieldNames = temp } else if opt == "--fill-with" { - fillWith = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fillWith, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerTemplateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fieldNames == nil { - transformerTemplateUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f or -t is required") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerTemplate( @@ -104,11 +111,10 @@ func transformerTemplateParseCLI( fillWith, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerTemplate struct { diff --git a/pkg/transformers/top.go b/pkg/transformers/top.go index cf43c197a..a02648450 100644 --- a/pkg/transformers/top.go +++ b/pkg/transformers/top.go @@ -52,7 +52,7 @@ func transformerTopParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -67,6 +67,7 @@ func transformerTopParseCLI( doMax := true outputFieldName := verbTopDefaultOutputFieldName + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -79,14 +80,23 @@ func transformerTopParseCLI( if opt == "-h" || opt == "--help" { transformerTopUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-n" { - topCount = cli.VerbGetIntArgOrDie(verb, opt, args, &argi, argc) + topCount, err = cli.VerbGetIntArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - valueFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + valueFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-g" { - groupByFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + groupByFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-a" { showFullRecords = true } else if opt == "--max" { @@ -96,26 +106,26 @@ func transformerTopParseCLI( } else if opt == "-F" { // Ignored in Miller 6; allowed for command-line backward compatibility } else if opt == "-o" { - outputFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerTopUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if valueFieldNames == nil { - transformerTopUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-f field names required") } if len(valueFieldNames) > 1 && showFullRecords { - transformerTopUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-a (full records) incompatible with multiple -f fields") } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, _ := NewTransformerTop( @@ -127,7 +137,7 @@ func transformerTopParseCLI( outputFieldName, ) - return transformer + return transformer, nil } type TransformerTop struct { diff --git a/pkg/transformers/unflatten.go b/pkg/transformers/unflatten.go index 6aad5a225..4b40b0b4c 100644 --- a/pkg/transformers/unflatten.go +++ b/pkg/transformers/unflatten.go @@ -39,7 +39,7 @@ func transformerUnflattenParseCLI( args []string, options *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -49,6 +49,7 @@ func transformerUnflattenParseCLI( oFlatSep := "" // means take it from the record context var fieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -61,23 +62,28 @@ func transformerUnflattenParseCLI( if opt == "-h" || opt == "--help" { transformerUnflattenUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-s" { - oFlatSep = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + oFlatSep, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerUnflattenUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerUnflatten( @@ -86,11 +92,10 @@ func transformerUnflattenParseCLI( fieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerUnflatten struct { diff --git a/pkg/transformers/uniq.go b/pkg/transformers/uniq.go index ad368f81b..1ab36bb73 100644 --- a/pkg/transformers/uniq.go +++ b/pkg/transformers/uniq.go @@ -57,7 +57,7 @@ func transformerCountDistinctParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -71,6 +71,7 @@ func transformerCountDistinctParseCLI( outputFieldName := uniqDefaultOutputFieldName doLashed := true + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -83,37 +84,43 @@ func transformerCountDistinctParseCLI( if opt == "-h" || opt == "--help" { transformerCountDistinctUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-g" || opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-x" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } invertFieldNames = true } else if opt == "-n" { showNumDistinctOnly = true } else if opt == "-o" { - outputFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-u" { doLashed = false } else { - transformerCountDistinctUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if fieldNames == nil { - transformerCountDistinctUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-g or -x field names required") } if !doLashed && showNumDistinctOnly { - transformerCountDistinctUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-n requires -a (uniqify entire records)") } showCounts := true @@ -121,7 +128,7 @@ func transformerCountDistinctParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerUniq( @@ -134,11 +141,10 @@ func transformerCountDistinctParseCLI( uniqifyEntireRecords, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } func transformerUniqUsage( @@ -168,7 +174,7 @@ func transformerUniqParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -183,6 +189,7 @@ func transformerUniqParseCLI( outputFieldName := uniqDefaultOutputFieldName uniqifyEntireRecords := false + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -195,13 +202,19 @@ func transformerUniqParseCLI( if opt == "-h" || opt == "--help" { transformerUniqUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-g" || opt == "-f" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-x" { - fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + fieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } invertFieldNames = true } else if opt == "-c" { @@ -211,30 +224,29 @@ func transformerUniqParseCLI( showNumDistinctOnly = true } else if opt == "-o" { - outputFieldName = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + outputFieldName, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-a" { uniqifyEntireRecords = true } else { - transformerUniqUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } if uniqifyEntireRecords { if fieldNames != nil { - transformerUniqUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-a (uniqify entire records) is incompatible with -g/-x") } if showCounts && showNumDistinctOnly { - transformerUniqUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-c and -n are mutually exclusive with -a") } } else { if fieldNames == nil { - transformerUniqUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "-g or -x field names required") } } @@ -242,7 +254,7 @@ func transformerUniqParseCLI( *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, _ := NewTransformerUniq( @@ -255,7 +267,7 @@ func transformerUniqParseCLI( uniqifyEntireRecords, ) - return transformer + return transformer, nil } type TransformerUniq struct { diff --git a/pkg/transformers/unspace.go b/pkg/transformers/unspace.go index c3bf8bf0c..c78781444 100644 --- a/pkg/transformers/unspace.go +++ b/pkg/transformers/unspace.go @@ -37,7 +37,7 @@ func transformerUnspaceParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -47,6 +47,7 @@ func transformerUnspaceParseCLI( filler := "_" which := "keys_and_values" + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -59,10 +60,13 @@ func transformerUnspaceParseCLI( if opt == "-h" || opt == "--help" { transformerUnspaceUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "-f" { - filler = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + filler, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-k" { which = "keys_only" @@ -71,23 +75,21 @@ func transformerUnspaceParseCLI( which = "values_only" } else { - transformerUnspaceUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerUnspace(filler, which) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerUnspace struct { diff --git a/pkg/transformers/unsparsify.go b/pkg/transformers/unsparsify.go index d558a819d..4a3226945 100644 --- a/pkg/transformers/unsparsify.go +++ b/pkg/transformers/unsparsify.go @@ -50,7 +50,7 @@ func transformerUnsparsifyParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -60,6 +60,7 @@ func transformerUnsparsifyParseCLI( fillerString := "" var specifiedFieldNames []string = nil + var err error for argi < argc /* variable increment: 1 or 2 depending on flag */ { opt := args[argi] if !strings.HasPrefix(opt, "-") { @@ -72,23 +73,28 @@ func transformerUnsparsifyParseCLI( if opt == "-h" || opt == "--help" { transformerUnsparsifyUsage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } else if opt == "--fill-with" { - fillerString = cli.VerbGetStringArgOrDie(verb, opt, args, &argi, argc) + fillerString, err = cli.VerbGetStringArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else if opt == "-f" { - specifiedFieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc) + specifiedFieldNames, err = cli.VerbGetStringArrayArg(verb, opt, args, &argi, argc) + if err != nil { + return nil, err + } } else { - transformerUnsparsifyUsage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verb, "option \"%s\" not recognized", opt) } } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerUnsparsify( @@ -96,11 +102,10 @@ func transformerUnsparsifyParseCLI( specifiedFieldNames, ) if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerUnsparsify struct { diff --git a/pkg/transformers/utf8_to_latin1.go b/pkg/transformers/utf8_to_latin1.go index e57db15f7..f5d5735c4 100644 --- a/pkg/transformers/utf8_to_latin1.go +++ b/pkg/transformers/utf8_to_latin1.go @@ -36,7 +36,7 @@ func transformerUTF8ToLatin1ParseCLI( args []string, _ *cli.TOptions, doConstruct bool, // false for first pass of CLI-parse, true for second pass -) RecordTransformer { +) (RecordTransformer, error) { // Skip the verb name from the current spot in the mlr command line argi := *pargi @@ -54,25 +54,23 @@ func transformerUTF8ToLatin1ParseCLI( if opt == "-h" || opt == "--help" { transformerUTF8ToLatin1Usage(os.Stdout) - os.Exit(0) + return nil, cli.ErrHelpRequested } - transformerUTF8ToLatin1Usage(os.Stderr) - os.Exit(1) + return nil, cli.VerbErrorf(verbNameUTF8ToLatin1, "option \"%s\" not recognized", opt) } *pargi = argi if !doConstruct { // All transformers must do this for main command-line parsing - return nil + return nil, nil } transformer, err := NewTransformerUTF8ToLatin1() if err != nil { - fmt.Fprintf(os.Stderr, "mlr: %v\n", err) - os.Exit(1) + return nil, err } - return transformer + return transformer, nil } type TransformerUTF8ToLatin1 struct {