diff --git a/cmd/del.go b/cmd/del.go index 23f1dab..b30432e 100644 --- a/cmd/del.go +++ b/cmd/del.go @@ -22,8 +22,8 @@ THE SOFTWARE. package cmd import ( + "errors" "fmt" - "os" "strings" "github.com/dgraph-io/badger/v4" @@ -32,11 +32,12 @@ import ( // delCmd represents the set command var delCmd = &cobra.Command{ - Use: "del KEY[@DB]", - Short: "Delete a key. Optionally specify a db.", - Aliases: []string{"delete", "rm", "remove"}, - Args: cobra.ExactArgs(1), - RunE: del, + Use: "del KEY[@DB]", + Short: "Delete a key. Optionally specify a db.", + Aliases: []string{"delete", "rm", "remove"}, + Args: cobra.ExactArgs(1), + RunE: del, + SilenceUsage: true, } func del(cmd *cobra.Command, args []string) error { @@ -47,6 +48,14 @@ func del(cmd *cobra.Command, args []string) error { return err } + exists, err := keyExists(store, args[0]) + if err != nil { + return fmt.Errorf("cannot remove '%s': %v", args[0], err) + } + if !exists { + return fmt.Errorf("cannot remove '%s': No such key", args[0]) + } + targetKey, err := formatKeyForPrompt(store, args[0]) if err != nil { return err @@ -54,13 +63,12 @@ func del(cmd *cobra.Command, args []string) error { if !force { var confirm string - message := fmt.Sprintf("Are you sure you want to delete %q? (y/n)", targetKey) + message := fmt.Sprintf("remove %q: are you sure? (y/n)", targetKey) fmt.Println(message) if _, err := fmt.Scanln(&confirm); err != nil { - return err + return fmt.Errorf("cannot remove '%s': %v", args[0], err) } if strings.ToLower(confirm) != "y" { - fmt.Fprintf(os.Stderr, "Did not delete %q\n", targetKey) return nil } } @@ -70,7 +78,10 @@ func del(cmd *cobra.Command, args []string) error { readonly: false, sync: false, transact: func(tx *badger.Txn, k []byte) error { - return tx.Delete(k) + if err := tx.Delete(k); errors.Is(err, badger.ErrKeyNotFound) { + return nil + } + return fmt.Errorf("cannot remove '%s': %v", args[0], err) }, } @@ -82,6 +93,27 @@ func init() { rootCmd.AddCommand(delCmd) } +func keyExists(store *Store, arg string) (bool, error) { + var notFound bool + trans := TransactionArgs{ + key: arg, + readonly: true, + sync: false, + transact: func(tx *badger.Txn, k []byte) error { + if _, err := tx.Get(k); errors.Is(err, badger.ErrKeyNotFound) { + notFound = true + return nil + } else { + return err + } + }, + } + if err := store.Transaction(trans); err != nil { + return false, err + } + return !notFound, nil +} + func formatKeyForPrompt(store *Store, arg string) (string, error) { _, db, err := store.parse(arg, true) if err != nil { diff --git a/cmd/get.go b/cmd/get.go index 22d0044..05a7ad4 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -47,9 +47,10 @@ additional argument after the initial KEY being fetched. For example: pda set greeting 'Hello, {{ .NAME }}!' pda get greeting NAME=World`, - Aliases: []string{"g"}, - Args: cobra.MinimumNArgs(1), - RunE: get, + Aliases: []string{"g"}, + Args: cobra.MinimumNArgs(1), + RunE: get, + SilenceUsage: true, } func get(cmd *cobra.Command, args []string) error { @@ -73,30 +74,30 @@ func get(cmd *cobra.Command, args []string) error { } if err := store.Transaction(trans); err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } includeSecret, err := cmd.Flags().GetBool("secret") if err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } if meta&metaSecret != 0 && !includeSecret { - return fmt.Errorf("%q is marked secret; re-run with --secret to display it", args[0]) + return fmt.Errorf("cannot get '%s': marked as secret, run with --secret", args[0]) } binary, err := cmd.Flags().GetBool("include-binary") if err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } run, err := cmd.Flags().GetBool("run") if err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } noTemplate, err := cmd.Flags().GetBool("no-template") if err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } if !noTemplate { @@ -106,7 +107,7 @@ func get(cmd *cobra.Command, args []string) error { } v, err = applyTemplate(v, substitutions) if err != nil { - return err + return fmt.Errorf("cannot get '%s': %v", args[0], err) } } diff --git a/cmd/mv.go b/cmd/mv.go index 08991b3..9928c4a 100644 --- a/cmd/mv.go +++ b/cmd/mv.go @@ -71,7 +71,7 @@ func mv(cmd *cobra.Command, args []string) error { if _, err := tx.Get(k); err == nil { return fmt.Errorf("cannot move '%s': '%s' already exists > run with --force to overwrite", fromKey, toKey) } else if err != badger.ErrKeyNotFound { - return err + return fmt.Errorf("cannot move '%s': %v", fromKey, err) } } entry := badger.NewEntry(k, srcVal).WithMeta(srcMeta) diff --git a/cmd/set.go b/cmd/set.go index 565814f..7732f1d 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -22,6 +22,7 @@ THE SOFTWARE. package cmd import ( + "fmt" "io" "github.com/dgraph-io/badger/v4" @@ -42,9 +43,10 @@ For example: 'Hello, {{ default "World" .NAME }}' will default to World if NAME is blank. 'Hello, {{ require .NAME }}' will error if NAME is blank. '{{ enum .NAME "Alice" "Bob" }}' allows only NAME=Alice or NAME=Bob.`, - Aliases: []string{"s"}, - Args: cobra.RangeArgs(1, 2), - RunE: set, + Aliases: []string{"s"}, + Args: cobra.RangeArgs(1, 2), + RunE: set, + SilenceUsage: true, } func set(cmd *cobra.Command, args []string) error { @@ -56,18 +58,18 @@ func set(cmd *cobra.Command, args []string) error { } else { bytes, err := io.ReadAll(cmd.InOrStdin()) if err != nil { - return err + return fmt.Errorf("cannot set '%s': %v", args[0], err) } value = bytes } secret, err := cmd.Flags().GetBool("secret") if err != nil { - return err + return fmt.Errorf("cannot set '%s': %v", args[0], err) } ttl, err := cmd.Flags().GetDuration("ttl") if err != nil { - return err + return fmt.Errorf("cannot set '%s': %v", args[0], err) } trans := TransactionArgs{