feat: huge overhaul of messaging into FAIL, WARN, hint, ok, prompt, and progress types
This commit is contained in:
parent
6ccd801c89
commit
b52a5bfdb7
30 changed files with 192 additions and 96 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:]...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
31
cmd/init.go
31
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
95
cmd/msg.go
Normal file
95
cmd/msg.go
Normal file
|
|
@ -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, "', '"))
|
||||
}
|
||||
}
|
||||
|
|
@ -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" {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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" {
|
||||
|
|
|
|||
10
cmd/root.go
10
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
2
testdata/dump__glob__ok.ct
vendored
2
testdata/dump__glob__ok.ct
vendored
|
|
@ -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*'
|
||||
|
|
|
|||
2
testdata/get__err__with__invalid_db.ct
vendored
2
testdata/get__err__with__invalid_db.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
testdata/get__missing__err.ct
vendored
2
testdata/get__missing__err.ct
vendored
|
|
@ -1,2 +1,2 @@
|
|||
$ pda get foobar --> FAIL
|
||||
Error: cannot get 'foobar': No such key
|
||||
FAIL cannot get 'foobar': no such key
|
||||
|
|
|
|||
14
testdata/get__missing__err__with__any.ct
vendored
14
testdata/get__missing__err__with__any.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
3
testdata/invalid__err.ct
vendored
3
testdata/invalid__err.ct
vendored
|
|
@ -1,3 +1,2 @@
|
|||
$ pda invalidcmd --> FAIL
|
||||
Error: unknown command "invalidcmd" for "pda"
|
||||
Run 'pda --help' for usage.
|
||||
FAIL unknown command "invalidcmd" for "pda"
|
||||
|
|
|
|||
2
testdata/list__err__with__invalid_db.ct
vendored
2
testdata/list__err__with__invalid_db.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
testdata/list__glob__ok.ct
vendored
2
testdata/list__glob__ok.ct
vendored
|
|
@ -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*'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
4
testdata/remove__dedupe__ok.ct
vendored
4
testdata/remove__dedupe__ok.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
6
testdata/remove__glob__mixed__ok.ct
vendored
6
testdata/remove__glob__mixed__ok.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
5
testdata/remove__glob__ok.ct
vendored
5
testdata/remove__glob__ok.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
5
testdata/remove__multiple__ok.ct
vendored
5
testdata/remove__multiple__ok.ct
vendored
|
|
@ -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'?
|
||||
|
|
|
|||
4
testdata/restore__drop__ok.ct
vendored
4
testdata/restore__drop__ok.ct
vendored
|
|
@ -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
|
||||
|
|
|
|||
7
testdata/restore__glob__ok.ct
vendored
7
testdata/restore__glob__ok.ct
vendored
|
|
@ -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*'
|
||||
|
|
|
|||
2
testdata/set__err__with__invalid-ttl.ct
vendored
2
testdata/set__err__with__invalid-ttl.ct
vendored
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue