diff --git a/builtins.go b/builtins.go index 883f34d..c6d6c0d 100644 --- a/builtins.go +++ b/builtins.go @@ -225,7 +225,7 @@ func floatArgs(_ map[string]node, a []string) error { } func seqArg(_ map[string]node, a []string) error { if len(a) > 1 { - return fmt.Errorf("seq takes at most one name, got %d args", len(a)) + return fmt.Errorf("seq takes at most one name, got %d", len(a)) } if len(a) == 1 && a[0] == "" { return fmt.Errorf("seq name must not be empty") diff --git a/cmd/fejkdata/main.go b/cmd/fejkdata/main.go index 88b9e69..cf8c96c 100644 --- a/cmd/fejkdata/main.go +++ b/cmd/fejkdata/main.go @@ -282,7 +282,7 @@ func (in invocation) check() (argKind, error) { return argPath, nil } if len(in.paths) != 1 { - return argPath, fmt.Errorf("expected one path or template, got %d", len(in.paths)) + return argPath, fmt.Errorf("expected one path or template, got %d%s", len(in.paths), templateSplit(in.paths)) } return classify(in.paths[0]) } @@ -478,11 +478,25 @@ func run(args []string, stdout, stderr io.Writer) int { return misuse(stderr, te.error) } fmt.Fprintln(stderr, err) + if errors.Is(err, fejkdata.ErrNoColumns) { + fmt.Fprintln(stderr, "wrap that JSON in single quotes, which keep a shell from expanding its braces") + } return 1 } return 0 } +// templateSplit names the cure for a template a shell split on its spaces, which is +// what several arguments carrying a token mean. +func templateSplit(paths []string) string { + for _, p := range paths { + if strings.Contains(p, "{") { + return `; a template's spaces split the argument, so wrap the whole argument in "…"` + } + } + return "" +} + func misuse(stderr io.Writer, err error) int { // A library error already names the program, so the prefix is not doubled. fmt.Fprintf(stderr, "fejkdata: %s\ntry 'fejkdata --help'\n", strings.TrimPrefix(err.Error(), "fejkdata: ")) diff --git a/data.go b/data.go index fab7a0e..b6549b1 100644 --- a/data.go +++ b/data.go @@ -43,7 +43,7 @@ func loadData(sources []dataSource) (map[string]node, error) { } info, err := os.Stat(src.path) if err != nil { - return nil, err + return nil, fmt.Errorf("data path %s: %w", src.path, err) } if !info.IsDir() { return nil, fmt.Errorf("%s is not a directory", src.path) diff --git a/record.go b/record.go index c413773..9a2621d 100644 --- a/record.go +++ b/record.go @@ -140,7 +140,7 @@ func (f *Generator) FakeRecord(path string) (*Record, error) { return nil, fmt.Errorf("fejkdata: %s descends into %q, a field; only a category-level template is a record", path, tail[0]) } shape := f.recordShapeOf(n) - if errors.Is(shape.err, errNoColumns) { + if errors.Is(shape.err, ErrNoColumns) { ns := names(segments) return nil, fmt.Errorf(`fejkdata: %s %w; render it as a column of one: {"format":"","%s":"{/%s}"}`, path, shape.err, ns[len(ns)-1], path) } @@ -231,9 +231,9 @@ func (f *Generator) FakeRecordTemplate(input string) (*Record, error) { return t.Fake(), nil } -// errNoColumns is the one record fence a path can answer, so its entry point names +// ErrNoColumns is the one record fence a path can answer, so its entry point names // the record to write instead. -var errNoColumns = errors.New("has no fields, so no columns") +var ErrNoColumns = errors.New("has no fields, so no columns") // recordOf is the fence both record entry points pass. The columns come back with // the template, fixed for every draw the caller goes on to make. @@ -247,7 +247,7 @@ func recordOf(n node) (*template, []Column, error) { } names := recordColumns(t) if len(names) == 0 { - return nil, nil, errNoColumns + return nil, nil, ErrNoColumns } if !t.record { return nil, nil, fmt.Errorf("carries repeat %d, which composes its format into one string; a record projects columns instead — drop the repeat and render the record again for more rows", t.repeat) diff --git a/struct.go b/struct.go index 891f820..617a460 100644 --- a/struct.go +++ b/struct.go @@ -340,7 +340,7 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro return fmt.Errorf("%s (%s): %s", label, ft, reason) } if !kind.holds(v) { - return fmt.Errorf("%s (%s): %q is not proven within %s; make it %s", label, ft, it.format, elem.Kind(), kind.wider) + return fmt.Errorf("%s (%s): %q is not proven within %s; narrow it to that range, or make the field %s", label, ft, it.format, elem.Kind(), kind.wider) } } return nil