From b52a5bfdb707cd2093d14b64adc70c76986ff096 Mon Sep 17 00:00:00 2001 From: lew Date: Wed, 11 Feb 2026 02:11:58 +0000 Subject: [PATCH] feat: huge overhaul of messaging into FAIL, WARN, hint, ok, prompt, and progress types --- cmd/del-db.go | 17 ++-- cmd/del.go | 9 +- cmd/get.go | 8 +- cmd/init.go | 31 +++--- cmd/list-dbs.go | 2 +- cmd/list.go | 8 +- cmd/msg.go | 95 +++++++++++++++++++ cmd/mv.go | 6 +- cmd/ndjson.go | 6 +- cmd/restore.go | 8 +- cmd/root.go | 10 +- cmd/set.go | 4 +- cmd/shared.go | 10 +- cmd/sync.go | 6 +- cmd/vcs.go | 6 +- testdata/dump__glob__ok.ct | 2 +- testdata/get__err__with__invalid_db.ct | 2 +- testdata/get__missing__err.ct | 2 +- testdata/get__missing__err__with__any.ct | 14 +-- testdata/invalid__err.ct | 3 +- testdata/list__err__with__invalid_db.ct | 2 +- testdata/list__glob__ok.ct | 2 +- .../remove-store__err__with__invalid_db.ct | 2 +- testdata/remove__dedupe__ok.ct | 4 +- testdata/remove__glob__mixed__ok.ct | 6 +- testdata/remove__glob__ok.ct | 5 +- testdata/remove__multiple__ok.ct | 5 +- testdata/restore__drop__ok.ct | 4 +- testdata/restore__glob__ok.ct | 7 +- testdata/set__err__with__invalid-ttl.ct | 2 +- 30 files changed, 192 insertions(+), 96 deletions(-) create mode 100644 cmd/msg.go diff --git a/cmd/del-db.go b/cmd/del-db.go index 334277b..1a254da 100644 --- a/cmd/del-db.go +++ b/cmd/del-db.go @@ -45,29 +45,28 @@ func delStore(cmd *cobra.Command, args []string) error { store := &Store{} dbName, err := store.parseDB(args[0], false) if err != nil { - return fmt.Errorf("cannot delete-store '%s': %v", args[0], err) + return fmt.Errorf("cannot delete store '%s': %v", args[0], err) } var notFound errNotFound path, err := store.FindStore(dbName) if errors.As(err, ¬Found) { - return fmt.Errorf("cannot delete-store '%s': %v", dbName, err) + return fmt.Errorf("cannot delete store '%s': %w", dbName, err) } if err != nil { - return fmt.Errorf("cannot delete-store '%s': %v", dbName, err) + return fmt.Errorf("cannot delete store '%s': %v", dbName, err) } interactive, err := cmd.Flags().GetBool("interactive") if err != nil { - return fmt.Errorf("cannot delete-store '%s': %v", dbName, err) + return fmt.Errorf("cannot delete store '%s': %v", dbName, err) } if interactive || config.Store.AlwaysPromptDelete { - message := fmt.Sprintf("delete-store '%s': are you sure? (y/n)", args[0]) - fmt.Println(message) + promptf("delete store '%s'? (y/n)", args[0]) var confirm string - if _, err := fmt.Scanln(&confirm); err != nil { - return fmt.Errorf("cannot delete-store '%s': %v", dbName, err) + if err := scanln(&confirm); err != nil { + return fmt.Errorf("cannot delete store '%s': %v", dbName, err) } if strings.ToLower(confirm) != "y" { return nil @@ -81,7 +80,7 @@ func delStore(cmd *cobra.Command, args []string) error { func executeDeletion(path string) error { if err := os.Remove(path); err != nil { - return fmt.Errorf("cannot delete-store '%s': %v", path, err) + return fmt.Errorf("cannot delete store '%s': %v", path, err) } return nil } diff --git a/cmd/del.go b/cmd/del.go index fac2fcd..8ab21d3 100644 --- a/cmd/del.go +++ b/cmd/del.go @@ -66,7 +66,7 @@ func del(cmd *cobra.Command, args []string) error { } if len(targets) == 0 { - return fmt.Errorf("cannot remove: No such key") + return fmt.Errorf("cannot remove: no such key") } // Group targets by store for batch deletes. @@ -78,9 +78,8 @@ func del(cmd *cobra.Command, args []string) error { for _, target := range targets { if interactive || config.Key.AlwaysPromptDelete { var confirm string - message := fmt.Sprintf("remove %q: are you sure? (y/n)", target.display) - fmt.Println(message) - if _, err := fmt.Scanln(&confirm); err != nil { + promptf("remove '%s'? (y/n)", target.display) + if err := scanln(&confirm); err != nil { return fmt.Errorf("cannot remove '%s': %v", target.full, err) } if strings.ToLower(confirm) != "y" { @@ -111,7 +110,7 @@ func del(cmd *cobra.Command, args []string) error { for _, t := range st.targets { idx := findEntry(entries, t.key) if idx < 0 { - return fmt.Errorf("cannot remove '%s': No such key", t.full) + return fmt.Errorf("cannot remove '%s': no such key", t.full) } entries = append(entries[:idx], entries[idx+1:]...) } diff --git a/cmd/get.go b/cmd/get.go index 6350d69..036ac4d 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -90,7 +90,7 @@ func get(cmd *cobra.Command, args []string) error { for i, e := range entries { keys[i] = e.Key } - return fmt.Errorf("cannot get '%s': %v", args[0], suggestKey(spec.Key, keys)) + return fmt.Errorf("cannot get '%s': %w", args[0], suggestKey(spec.Key, keys)) } v := entries[idx].Value @@ -128,7 +128,7 @@ func applyTemplate(tplBytes []byte, substitutions []string) ([]byte, error) { for _, s := range substitutions { parts := strings.SplitN(s, "=", 2) if len(parts) != 2 || parts[0] == "" { - fmt.Fprintf(os.Stderr, "invalid substitutions %q (expected KEY=VALUE)\n", s) + warnf("invalid substitution '%s', expected KEY=VALUE", s) continue } key := parts[0] @@ -159,13 +159,13 @@ func applyTemplate(tplBytes []byte, substitutions []string) ([]byte, error) { if slices.Contains(allowed, s) { return s, nil } - return "", fmt.Errorf("invalid value %q (allowed: %v)", s, allowed) + return "", fmt.Errorf("invalid value '%s', allowed: %v", s, allowed) }, "int": func(v any) (int, error) { s := fmt.Sprint(v) i, err := strconv.Atoi(s) if err != nil { - return 0, fmt.Errorf("failed to convert to int: %w", err) + return 0, fmt.Errorf("cannot convert to int: %w", err) } return i, nil }, diff --git a/cmd/init.go b/cmd/init.go index 764e247..bec7472 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -61,36 +61,36 @@ func vcsInit(cmd *cobra.Command, args []string) error { if clean { gitDir := filepath.Join(repoDir, ".git") if _, err := os.Stat(gitDir); err == nil { - fmt.Printf("remove .git from '%s'? (y/n)\n", repoDir) + promptf("remove .git from '%s'? (y/n)", repoDir) var confirm string - if _, err := fmt.Scanln(&confirm); err != nil { - return fmt.Errorf("cannot clean git dir: %w", err) + if err := scanln(&confirm); err != nil { + return fmt.Errorf("cannot init: %w", err) } if strings.ToLower(confirm) != "y" { - return fmt.Errorf("aborted cleaning git dir") + return fmt.Errorf("cannot init: aborted") } if err := os.RemoveAll(gitDir); err != nil { - return fmt.Errorf("cannot clean git dir: %w", err) + return fmt.Errorf("cannot init: %w", err) } } if hasRemote { dbs, err := store.AllStores() if err == nil && len(dbs) > 0 { - fmt.Printf("remove all existing stores and .gitignore? (required for clone) (y/n)\n") + promptf("remove all existing stores and .gitignore, required for clone? (y/n)") var confirm string - if _, err := fmt.Scanln(&confirm); err != nil { - return fmt.Errorf("cannot clean stores: %w", err) + if err := scanln(&confirm); err != nil { + return fmt.Errorf("cannot init: %w", err) } if strings.ToLower(confirm) != "y" { - return fmt.Errorf("aborted cleaning stores") + return fmt.Errorf("cannot init: aborted") } if err := wipeAllStores(store); err != nil { - return fmt.Errorf("cannot clean stores: %w", err) + return fmt.Errorf("cannot init: %w", err) } gi := filepath.Join(repoDir, ".gitignore") if err := os.Remove(gi); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("cannot remove .gitignore: %w", err) + return fmt.Errorf("cannot init: %w", err) } } } @@ -98,7 +98,8 @@ func vcsInit(cmd *cobra.Command, args []string) error { gitDir := filepath.Join(repoDir, ".git") if _, err := os.Stat(gitDir); err == nil { - fmt.Println("vcs already initialised; use --clean to reinitialise") + warnf("vcs already initialised") + printHint("use --clean to reinitialise") return nil } @@ -106,11 +107,11 @@ func vcsInit(cmd *cobra.Command, args []string) error { // git clone requires the target directory to be empty entries, err := os.ReadDir(repoDir) if err == nil && len(entries) > 0 { - return fmt.Errorf("stores directory is not empty; use --clean with a remote to wipe and clone") + return withHint(fmt.Errorf("cannot init: stores directory not empty"), "use --clean with a remote to wipe and clone") } remote := args[0] - fmt.Printf("running: git clone %s %s\n", remote, repoDir) + progressf("git clone %s %s", remote, repoDir) if err := runGit("", "clone", remote, repoDir); err != nil { return err } @@ -118,7 +119,7 @@ func vcsInit(cmd *cobra.Command, args []string) error { if err := os.MkdirAll(repoDir, 0o750); err != nil { return err } - fmt.Printf("running: git init\n") + progressf("git init") if err := runGit(repoDir, "init"); err != nil { return err } diff --git a/cmd/list-dbs.go b/cmd/list-dbs.go index 7e36f29..2ff7a22 100644 --- a/cmd/list-dbs.go +++ b/cmd/list-dbs.go @@ -41,7 +41,7 @@ func listStores(cmd *cobra.Command, args []string) error { store := &Store{} dbs, err := store.AllStores() if err != nil { - return fmt.Errorf("cannot list-stores: %v", err) + return fmt.Errorf("cannot list stores: %v", err) } for _, db := range dbs { fmt.Println("@" + db) diff --git a/cmd/list.go b/cmd/list.go index 88779f4..3d26103 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -47,7 +47,7 @@ func (e *formatEnum) Set(v string) error { *e = formatEnum(v) return nil default: - return fmt.Errorf("must be one of \"table\", \"tsv\", \"csv\", \"html\", \"markdown\", or \"ndjson\"") + return fmt.Errorf("must be one of 'table', 'tsv', 'csv', 'html', 'markdown', or 'ndjson'") } } @@ -91,7 +91,7 @@ func list(cmd *cobra.Command, args []string) error { if _, err := store.FindStore(dbName); err != nil { var notFound errNotFound if errors.As(err, ¬Found) { - return fmt.Errorf("cannot ls '%s': No such store", args[0]) + return fmt.Errorf("cannot ls '%s': %w", args[0], err) } return fmt.Errorf("cannot ls '%s': %v", args[0], err) } @@ -99,7 +99,7 @@ func list(cmd *cobra.Command, args []string) error { } if listNoKeys && listNoValues && !listTTL { - return fmt.Errorf("cannot ls '%s': no columns selected; disable --no-keys/--no-values or pass --ttl", targetDB) + return withHint(fmt.Errorf("cannot ls '%s': no columns selected", targetDB), "disable --no-keys/--no-values or pass --ttl") } var columns []columnKind @@ -145,7 +145,7 @@ func list(cmd *cobra.Command, args []string) error { } if len(matchers) > 0 && len(filtered) == 0 { - return fmt.Errorf("cannot ls '%s': No matches for pattern %s", targetDB, formatGlobPatterns(globPatterns)) + return fmt.Errorf("cannot ls '%s': no matches for pattern %s", targetDB, formatGlobPatterns(globPatterns)) } output := cmd.OutOrStdout() diff --git a/cmd/msg.go b/cmd/msg.go new file mode 100644 index 0000000..21a44ed --- /dev/null +++ b/cmd/msg.go @@ -0,0 +1,95 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + "strings" + + "golang.org/x/term" +) + +// hinted wraps an error with an actionable hint shown on a separate line. +type hinted struct { + err error + hint string +} + +func (h hinted) Error() string { return h.err.Error() } +func (h hinted) Unwrap() error { return h.err } + +func withHint(err error, hint string) error { + return hinted{err: err, hint: hint} +} + +func stderrIsTerminal() bool { + return term.IsTerminal(int(os.Stderr.Fd())) +} + +func stdoutIsTerminal() bool { + return term.IsTerminal(int(os.Stdout.Fd())) +} + +// keyword returns a right-aligned, colored keyword (color only on TTY). +// +// FAIL red (stderr) +// hint dim (stderr) +// WARN yellow (stderr) +// ok green (stderr) +// ? cyan (stdout) +// > dim (stdout) +func keyword(code, word string, tty bool) string { + padded := fmt.Sprintf("%4s", word) + if tty { + return fmt.Sprintf("\033[%sm%s\033[0m", code, padded) + } + return padded +} + +func printError(err error) { + fmt.Fprintf(os.Stderr, "%s %s\n", keyword("31", "FAIL", stderrIsTerminal()), err) +} + +func printHint(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stderr, "%s %s\n", keyword("2", "hint", stderrIsTerminal()), msg) +} + +func warnf(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stderr, "%s %s\n", keyword("33", "WARN", stderrIsTerminal()), msg) +} + +func okf(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stderr, "%s %s\n", keyword("32", "ok", stderrIsTerminal()), msg) +} + +func promptf(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stdout, "%s %s\n", keyword("36", "???", stdoutIsTerminal()), msg) +} + +func progressf(format string, args ...any) { + msg := fmt.Sprintf(format, args...) + fmt.Fprintf(os.Stdout, "%s %s\n", keyword("2", ">", stdoutIsTerminal()), msg) +} + +func scanln(dest *string) error { + fmt.Fprintf(os.Stdout, "%s ", keyword("2", "==>", stdoutIsTerminal())) + _, err := fmt.Scanln(dest) + return err +} + +// printErrorWithHints prints the error and any hints found in the error chain. +func printErrorWithHints(err error) { + printError(err) + var h hinted + if errors.As(err, &h) { + printHint("%s", h.hint) + } + var nf errNotFound + if errors.As(err, &nf) && len(nf.suggestions) > 0 { + printHint("did you mean '%s'?", strings.Join(nf.suggestions, "', '")) + } +} diff --git a/cmd/mv.go b/cmd/mv.go index 6ba3bbf..c6e1cd0 100644 --- a/cmd/mv.go +++ b/cmd/mv.go @@ -84,7 +84,7 @@ func mvImpl(cmd *cobra.Command, args []string, keepSource bool) error { } srcIdx := findEntry(srcEntries, fromSpec.Key) if srcIdx < 0 { - return fmt.Errorf("cannot move '%s': No such key", fromSpec.Key) + return fmt.Errorf("cannot move '%s': no such key", fromSpec.Key) } srcEntry := srcEntries[srcIdx] @@ -108,8 +108,8 @@ func mvImpl(cmd *cobra.Command, args []string, keepSource bool) error { if promptOverwrite && dstIdx >= 0 { var confirm string - fmt.Printf("overwrite '%s'? (y/n)\n", toSpec.Display()) - if _, err := fmt.Scanln(&confirm); err != nil { + promptf("overwrite '%s'? (y/n)", toSpec.Display()) + if err := scanln(&confirm); err != nil { return fmt.Errorf("cannot move '%s': %v", fromSpec.Key, err) } if strings.ToLower(confirm) != "y" { diff --git a/cmd/ndjson.go b/cmd/ndjson.go index 458b5a4..9e737bb 100644 --- a/cmd/ndjson.go +++ b/cmd/ndjson.go @@ -116,7 +116,7 @@ func writeStoreFile(path string, entries []Entry) error { je := encodeJsonEntry(e) data, err := json.Marshal(je) if err != nil { - return fmt.Errorf("key %q: %w", e.Key, err) + return fmt.Errorf("key '%s': %w", e.Key, err) } w.Write(data) w.WriteByte('\n') @@ -142,10 +142,10 @@ func decodeJsonEntry(je jsonEntry) (Entry, error) { var err error value, err = base64.StdEncoding.DecodeString(je.Value) if err != nil { - return Entry{}, fmt.Errorf("decode base64 for %q: %w", je.Key, err) + return Entry{}, fmt.Errorf("decode base64 for '%s': %w", je.Key, err) } default: - return Entry{}, fmt.Errorf("unsupported encoding %q for %q", je.Encoding, je.Key) + return Entry{}, fmt.Errorf("unsupported encoding '%s' for '%s'", je.Encoding, je.Key) } var expiresAt uint64 if je.ExpiresAt != nil { diff --git a/cmd/restore.go b/cmd/restore.go index e134822..2e33b75 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -104,10 +104,10 @@ func restore(cmd *cobra.Command, args []string) error { } if len(matchers) > 0 && restored == 0 { - return fmt.Errorf("cannot restore '%s': No matches for pattern %s", displayTarget, formatGlobPatterns(globPatterns)) + return fmt.Errorf("cannot restore '%s': no matches for pattern %s", displayTarget, formatGlobPatterns(globPatterns)) } - fmt.Fprintf(cmd.ErrOrStderr(), "Restored %d entries into @%s\n", restored, dbName) + okf("restored %d entries into @%s", restored, dbName) return autoSync() } @@ -169,9 +169,9 @@ func restoreEntries(decoder *json.Decoder, storePath string, opts restoreOpts) ( idx := findEntry(existing, entry.Key) if opts.promptOverwrite && idx >= 0 { - fmt.Printf("overwrite '%s'? (y/n)\n", entry.Key) + promptf("overwrite '%s'? (y/n)", entry.Key) var confirm string - if _, err := fmt.Scanln(&confirm); err != nil { + if err := scanln(&confirm); err != nil { return 0, fmt.Errorf("entry %d: %v", entryNo, err) } if strings.ToLower(confirm) != "y" { diff --git a/cmd/root.go b/cmd/root.go index b4dd459..358ec06 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,18 +31,20 @@ import ( // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "pda", - Short: "A key-value store tool", - Long: asciiArt, + Use: "pda", + Short: "A key-value store tool", + Long: asciiArt, + SilenceErrors: true, // we print errors ourselves } func Execute() { if configErr != nil { - fmt.Fprintln(os.Stderr, "failed to load config:", configErr) + printError(fmt.Errorf("cannot load config: %v", configErr)) os.Exit(1) } err := rootCmd.Execute() if err != nil { + printErrorWithHints(err) os.Exit(1) } } diff --git a/cmd/set.go b/cmd/set.go index 99fce82..9f1a123 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -93,9 +93,9 @@ func set(cmd *cobra.Command, args []string) error { idx := findEntry(entries, spec.Key) if promptOverwrite && idx >= 0 { - fmt.Printf("overwrite '%s'? (y/n)\n", spec.Display()) + promptf("overwrite '%s'? (y/n)", spec.Display()) var confirm string - if _, err := fmt.Scanln(&confirm); err != nil { + if err := scanln(&confirm); err != nil { return fmt.Errorf("cannot set '%s': %v", args[0], err) } if strings.ToLower(confirm) != "y" { diff --git a/cmd/shared.go b/cmd/shared.go index 43c35f9..511b673 100644 --- a/cmd/shared.go +++ b/cmd/shared.go @@ -37,14 +37,12 @@ import ( ) type errNotFound struct { + what string // "key" or "store" suggestions []string } func (err errNotFound) Error() string { - if len(err.suggestions) == 0 { - return "No such key" - } - return fmt.Sprintf("No such key. Did you mean '%s'?", strings.Join(err.suggestions, ", ")) + return fmt.Sprintf("no such %s", err.what) } type Store struct{} @@ -129,7 +127,7 @@ func (s *Store) FindStore(k string) (string, error) { if err != nil { return "", err } - return "", errNotFound{suggestions} + return "", errNotFound{what: "store", suggestions: suggestions} } if statErr != nil { return "", statErr @@ -205,7 +203,7 @@ func suggestKey(target string, keys []string) error { suggestions = append(suggestions, k) } } - return errNotFound{suggestions} + return errNotFound{what: "key", suggestions: suggestions} } func ensureSubpath(base, target string) error { diff --git a/cmd/sync.go b/cmd/sync.go index e325701..510a5dd 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -67,12 +67,12 @@ func sync(manual bool) error { return err } } else if manual { - fmt.Println("no changes to commit") + okf("no changes to commit") } if remoteInfo.Ref == "" { if manual { - fmt.Println("no remote configured; skipping push") + warnf("no remote configured, skipping push") } return nil } @@ -105,7 +105,7 @@ func sync(manual bool) error { return pushRemote(repoDir, remoteInfo) } if manual { - fmt.Println("nothing to push") + okf("nothing to push") } } diff --git a/cmd/vcs.go b/cmd/vcs.go index ecb1f45..3e0c22f 100644 --- a/cmd/vcs.go +++ b/cmd/vcs.go @@ -17,7 +17,7 @@ func ensureVCSInitialized() (string, error) { } if _, err := os.Stat(filepath.Join(repoDir, ".git")); err != nil { if os.IsNotExist(err) { - return "", fmt.Errorf("vcs repository not initialised; run 'pda init' first") + return "", withHint(fmt.Errorf("vcs not initialised"), "run 'pda init' first") } return "", err } @@ -43,7 +43,7 @@ func writeGitignore(repoDir string) error { } return runGit(repoDir, "commit", "-m", "generated gitignore") } - fmt.Println("Existing .gitignore found.") + okf("existing .gitignore found") return nil } @@ -195,7 +195,7 @@ func wipeAllStores(store *Store) error { return err } if err := os.Remove(p); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("remove store '%s': %w", db, err) + return fmt.Errorf("cannot remove store '%s': %w", db, err) } } return nil diff --git a/testdata/dump__glob__ok.ct b/testdata/dump__glob__ok.ct index 87ca9da..999d094 100644 --- a/testdata/dump__glob__ok.ct +++ b/testdata/dump__glob__ok.ct @@ -5,4 +5,4 @@ $ pda dump --glob a* {"key":"a1","value":"1","encoding":"text"} {"key":"a2","value":"2","encoding":"text"} $ pda dump --glob c* --> FAIL -Error: cannot ls '@default': No matches for pattern 'c*' +FAIL cannot ls '@default': no matches for pattern 'c*' diff --git a/testdata/get__err__with__invalid_db.ct b/testdata/get__err__with__invalid_db.ct index 6be4beb..973d83b 100644 --- a/testdata/get__err__with__invalid_db.ct +++ b/testdata/get__err__with__invalid_db.ct @@ -1,2 +1,2 @@ $ pda get key@foo/bar --> FAIL -Error: cannot get 'key@foo/bar': bad store format, use STORE or @STORE +FAIL cannot get 'key@foo/bar': bad store format, use STORE or @STORE diff --git a/testdata/get__missing__err.ct b/testdata/get__missing__err.ct index 0a54c6c..b528954 100644 --- a/testdata/get__missing__err.ct +++ b/testdata/get__missing__err.ct @@ -1,2 +1,2 @@ $ pda get foobar --> FAIL -Error: cannot get 'foobar': No such key +FAIL cannot get 'foobar': no such key diff --git a/testdata/get__missing__err__with__any.ct b/testdata/get__missing__err__with__any.ct index c942bcb..5f03ce9 100644 --- a/testdata/get__missing__err__with__any.ct +++ b/testdata/get__missing__err__with__any.ct @@ -5,10 +5,10 @@ $ pda get foobar --include-binary --run --secret --> FAIL $ pda get foobar --run --> FAIL $ pda get foobar --run --secret --> FAIL $ pda get foobar --secret --> FAIL -Error: cannot get 'foobar': No such key -Error: cannot get 'foobar': No such key -Error: cannot get 'foobar': No such key -Error: unknown flag: --secret -Error: cannot get 'foobar': No such key -Error: unknown flag: --secret -Error: unknown flag: --secret +FAIL cannot get 'foobar': no such key +FAIL cannot get 'foobar': no such key +FAIL cannot get 'foobar': no such key +FAIL unknown flag: --secret +FAIL cannot get 'foobar': no such key +FAIL unknown flag: --secret +FAIL unknown flag: --secret diff --git a/testdata/invalid__err.ct b/testdata/invalid__err.ct index 1bd0417..93359ea 100644 --- a/testdata/invalid__err.ct +++ b/testdata/invalid__err.ct @@ -1,3 +1,2 @@ $ pda invalidcmd --> FAIL -Error: unknown command "invalidcmd" for "pda" -Run 'pda --help' for usage. +FAIL unknown command "invalidcmd" for "pda" diff --git a/testdata/list__err__with__invalid_db.ct b/testdata/list__err__with__invalid_db.ct index f53a448..0a90ecb 100644 --- a/testdata/list__err__with__invalid_db.ct +++ b/testdata/list__err__with__invalid_db.ct @@ -1,2 +1,2 @@ $ pda ls foo/bar --> FAIL -Error: cannot ls 'foo/bar': cannot parse store: bad store format, use STORE or @STORE +FAIL cannot ls 'foo/bar': cannot parse store: bad store format, use STORE or @STORE diff --git a/testdata/list__glob__ok.ct b/testdata/list__glob__ok.ct index 924d72c..ece552c 100644 --- a/testdata/list__glob__ok.ct +++ b/testdata/list__glob__ok.ct @@ -7,4 +7,4 @@ a2 2 $ pda ls lg --glob b* --format tsv b1 3 $ pda ls lg --glob c* --> FAIL -Error: cannot ls '@lg': No matches for pattern 'c*' +FAIL cannot ls '@lg': no matches for pattern 'c*' diff --git a/testdata/remove-store__err__with__invalid_db.ct b/testdata/remove-store__err__with__invalid_db.ct index 9d99bf7..6750010 100644 --- a/testdata/remove-store__err__with__invalid_db.ct +++ b/testdata/remove-store__err__with__invalid_db.ct @@ -1,2 +1,2 @@ $ pda rms foo/bar --> FAIL -Error: cannot delete-store 'foo/bar': cannot parse store: bad store format, use STORE or @STORE +FAIL cannot delete store 'foo/bar': cannot parse store: bad store format, use STORE or @STORE diff --git a/testdata/remove__dedupe__ok.ct b/testdata/remove__dedupe__ok.ct index e5ec064..9324b0c 100644 --- a/testdata/remove__dedupe__ok.ct +++ b/testdata/remove__dedupe__ok.ct @@ -10,6 +10,6 @@ $ pda ls foo 1 $ pda rm foo --glob "*" $ pda get bar --> FAIL -Error: cannot get 'bar': No such key +FAIL cannot get 'bar': no such key $ pda get foo --> FAIL -Error: cannot get 'foo': No such key +FAIL cannot get 'foo': no such key diff --git a/testdata/remove__glob__mixed__ok.ct b/testdata/remove__glob__mixed__ok.ct index 9433c89..3f5fa6b 100644 --- a/testdata/remove__glob__mixed__ok.ct +++ b/testdata/remove__glob__mixed__ok.ct @@ -3,8 +3,8 @@ $ pda set bar1 2 $ pda set bar2 3 $ pda rm foo --glob bar* $ pda get foo --> FAIL -Error: cannot get 'foo': No such key +FAIL cannot get 'foo': no such key $ pda get bar1 --> FAIL -Error: cannot get 'bar1': No such key +FAIL cannot get 'bar1': no such key $ pda get bar2 --> FAIL -Error: cannot get 'bar2': No such key +FAIL cannot get 'bar2': no such key diff --git a/testdata/remove__glob__ok.ct b/testdata/remove__glob__ok.ct index 3fda37c..7ad2534 100644 --- a/testdata/remove__glob__ok.ct +++ b/testdata/remove__glob__ok.ct @@ -3,8 +3,9 @@ $ pda set a2 2 $ pda set b1 3 $ pda rm --glob a* $ pda get a1 --> FAIL -Error: cannot get 'a1': No such key. Did you mean 'b1'? +FAIL cannot get 'a1': no such key +hint did you mean 'b1'? $ pda get a2 --> FAIL -Error: cannot get 'a2': No such key +FAIL cannot get 'a2': no such key $ pda get b1 3 diff --git a/testdata/remove__multiple__ok.ct b/testdata/remove__multiple__ok.ct index d61e113..a46876a 100644 --- a/testdata/remove__multiple__ok.ct +++ b/testdata/remove__multiple__ok.ct @@ -2,6 +2,7 @@ $ pda set a 1 $ pda set b 2 $ pda rm a b $ pda get a --> FAIL -Error: cannot get 'a': No such key +FAIL cannot get 'a': no such key $ pda get b --> FAIL -Error: cannot get 'b': No such key. Did you mean 'b1'? +FAIL cannot get 'b': no such key +hint did you mean 'b1'? diff --git a/testdata/restore__drop__ok.ct b/testdata/restore__drop__ok.ct index b5c18f1..3ddadb2 100644 --- a/testdata/restore__drop__ok.ct +++ b/testdata/restore__drop__ok.ct @@ -2,8 +2,8 @@ $ pda set existing keep-me $ pda set other also-keep $ fecho dumpfile {"key":"new","value":"hello","encoding":"text"} $ pda restore --drop --file dumpfile -Restored 1 entries into @default + ok restored 1 entries into @default $ pda get new hello $ pda get existing --> FAIL -Error: cannot get 'existing': No such key +FAIL cannot get 'existing': no such key diff --git a/testdata/restore__glob__ok.ct b/testdata/restore__glob__ok.ct index eae61f8..9f5f5da 100644 --- a/testdata/restore__glob__ok.ct +++ b/testdata/restore__glob__ok.ct @@ -4,12 +4,13 @@ $ pda set b1 3 $ fecho dumpfile {"key":"a1","value":"1","encoding":"text"} {"key":"a2","value":"2","encoding":"text"} {"key":"b1","value":"3","encoding":"text"} $ pda rm a1 a2 b1 $ pda restore --glob a* --file dumpfile -Restored 2 entries into @default + ok restored 2 entries into @default $ pda get a1 1 $ pda get a2 2 $ pda get b1 --> FAIL -Error: cannot get 'b1': No such key. Did you mean 'a1'? +FAIL cannot get 'b1': no such key +hint did you mean 'a1'? $ pda restore --glob c* --file dumpfile --> FAIL -Error: cannot restore '@default': No matches for pattern 'c*' +FAIL cannot restore '@default': no matches for pattern 'c*' diff --git a/testdata/set__err__with__invalid-ttl.ct b/testdata/set__err__with__invalid-ttl.ct index a27ea1d..9a33eef 100644 --- a/testdata/set__err__with__invalid-ttl.ct +++ b/testdata/set__err__with__invalid-ttl.ct @@ -1,2 +1,2 @@ $ pda set a b --ttl 3343r --> FAIL -Error: invalid argument "3343r" for "-t, --ttl" flag: time: unknown unit "r" in duration "3343r" +FAIL invalid argument "3343r" for "-t, --ttl" flag: time: unknown unit "r" in duration "3343r"