diff --git a/cmd/del.go b/cmd/del.go index 8979c9c..50ec4f2 100644 --- a/cmd/del.go +++ b/cmd/del.go @@ -139,17 +139,11 @@ func keyExists(store *Store, arg string) (bool, error) { } func formatKeyForPrompt(store *Store, arg string) (string, error) { - _, db, err := store.parse(arg, true) + spec, err := store.parseKey(arg, true) if err != nil { return "", err } - if strings.Contains(arg, "@") { - return arg, nil - } - if db == "" { - return arg, nil - } - return fmt.Sprintf("%s@%s", arg, db), nil + return spec.Display(), nil } func resolveDeleteTargets(store *Store, exactArgs []string, globPatterns []string, separators []rune) ([]string, []string, error) { @@ -188,18 +182,18 @@ func resolveDeleteTargets(store *Store, exactArgs []string, globPatterns []strin var compiled []compiledPattern for _, raw := range globPatterns { - kb, db, err := store.parse(raw, true) + spec, err := store.parseKey(raw, true) if err != nil { return nil, nil, err } - pattern := string(kb) + pattern := spec.Key m, err := glob.Compile(pattern, separators...) if err != nil { return nil, nil, fmt.Errorf("cannot remove '%s': %v", raw, err) } compiled = append(compiled, compiledPattern{ rawArg: raw, - db: db, + db: spec.DB, matcher: m, }) } diff --git a/cmd/keyspec.go b/cmd/keyspec.go new file mode 100644 index 0000000..330a43f --- /dev/null +++ b/cmd/keyspec.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "fmt" + "strings" +) + +// KeySpec is a parsed key reference. +type KeySpec struct { + Raw string // Whole, unmodified user input + RawKey string // Key segment + RawDB string // DB segment + Key string // Normalised Key + DB string // Normalised DB +} + +// ParseKey parses "KEY[@DB]" into a normalized KeySpec. +// When defaults is true, a missing DB defaults to "default". +func ParseKey(raw string, defaults bool) (KeySpec, error) { + parts := strings.Split(raw, "@") + if len(parts) > 2 { + return KeySpec{}, fmt.Errorf("bad key format, use KEY@DB") + } + + rawKey := parts[0] + rawDB := "" + if len(parts) == 2 { + rawDB = parts[1] + if strings.TrimSpace(rawDB) == "" { + return KeySpec{}, fmt.Errorf("bad key format, use KEY@DB") + } + } + + key := strings.ToLower(rawKey) + db := strings.ToLower(rawDB) + if db == "" && defaults { + db = "default" + } + + return KeySpec{ + Raw: raw, + RawKey: rawKey, + RawDB: rawDB, + Key: key, + DB: db, + }, nil +} + +// Full returns the whole normalized key reference. +func (k KeySpec) Full() string { + if k.DB == "" { + return k.Key + } + return fmt.Sprintf("%s@%s", k.Key, k.DB) +} + +// Display returns the normalized key reference +// but omits the default database if none was set manually +func (k KeySpec) Display() string { + if k.DB == "" || k.DB == "default" { + return k.Key + } + return fmt.Sprintf("%s@%s", k.Key, k.DB) +} diff --git a/cmd/mv.go b/cmd/mv.go index 9928c4a..c8f0e68 100644 --- a/cmd/mv.go +++ b/cmd/mv.go @@ -30,11 +30,11 @@ func cp(cmd *cobra.Command, args []string) error { func mv(cmd *cobra.Command, args []string) error { store := &Store{} - fromKey, fromDB, err := store.parse(args[0], true) + fromSpec, err := store.parseKey(args[0], true) if err != nil { return err } - toKey, toDB, err := store.parse(args[1], true) + toSpec, err := store.parseKey(args[1], true) if err != nil { return err } @@ -42,13 +42,16 @@ func mv(cmd *cobra.Command, args []string) error { var srcVal []byte var srcMeta byte var srcExpires uint64 + fromRef := fromSpec.Full() + toRef := toSpec.Full() + readErr := store.Transaction(TransactionArgs{ - key: fmt.Sprintf("%s@%s", fromKey, fromDB), + key: fromRef, readonly: true, transact: func(tx *badger.Txn, k []byte) error { item, err := tx.Get(k) if err != nil { - return fmt.Errorf("cannot move '%s': %v", fromKey, err) + return fmt.Errorf("cannot move '%s': %v", fromSpec.Key, err) } srcMeta = item.UserMeta() srcExpires = item.ExpiresAt() @@ -63,15 +66,15 @@ func mv(cmd *cobra.Command, args []string) error { } writeErr := store.Transaction(TransactionArgs{ - key: fmt.Sprintf("%s@%s", toKey, toDB), + key: toRef, readonly: false, sync: false, transact: func(tx *badger.Txn, k []byte) error { if !force { if _, err := tx.Get(k); err == nil { - return fmt.Errorf("cannot move '%s': '%s' already exists > run with --force to overwrite", fromKey, toKey) + return fmt.Errorf("cannot move '%s': '%s' already exists > run with --force to overwrite", fromSpec.Key, toSpec.Key) } else if err != badger.ErrKeyNotFound { - return fmt.Errorf("cannot move '%s': %v", fromKey, err) + return fmt.Errorf("cannot move '%s': %v", fromSpec.Key, err) } } entry := badger.NewEntry(k, srcVal).WithMeta(srcMeta) @@ -90,7 +93,7 @@ func mv(cmd *cobra.Command, args []string) error { } return store.Transaction(TransactionArgs{ - key: fmt.Sprintf("%s@%s", fromKey, fromDB), + key: fromRef, readonly: false, sync: false, transact: func(tx *badger.Txn, k []byte) error { diff --git a/cmd/shared.go b/cmd/shared.go index 4b14f50..9a0c791 100644 --- a/cmd/shared.go +++ b/cmd/shared.go @@ -61,12 +61,12 @@ type TransactionArgs struct { } func (s *Store) Transaction(args TransactionArgs) error { - k, dbName, err := s.parse(args.key, true) + spec, err := s.parseKey(args.key, true) if err != nil { return err } - db, err := s.open(dbName) + db, err := s.open(spec.DB) if err != nil { return err } @@ -82,7 +82,7 @@ func (s *Store) Transaction(args TransactionArgs) error { tx := db.NewTransaction(!args.readonly) defer tx.Discard() - if err := args.transact(tx, k); err != nil { + if err := args.transact(tx, []byte(spec.Key)); err != nil { return err } @@ -162,22 +162,8 @@ func (s *Store) FindStore(k string) (string, error) { return path, nil } -func (s *Store) parse(k string, defaults bool) ([]byte, string, error) { - var key, db string - ps := strings.Split(k, "@") - switch len(ps) { - case 1: - key = strings.ToLower(ps[0]) - if defaults { - db = "default" - } - case 2: - key = strings.ToLower(ps[0]) - db = strings.ToLower(ps[1]) - default: - return nil, "", fmt.Errorf("bad key format, use KEY@DB") - } - return []byte(key), db, nil +func (s *Store) parseKey(raw string, defaults bool) (KeySpec, error) { + return ParseKey(raw, defaults) } func (s *Store) parseDB(v string, defaults bool) (string, error) {