2021-11-17 03:38:42 +00:00
|
|
|
package kvdriver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/diamondburned/arikawa/v3/utils/json"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ByteCodec describes a pair of marshaler and unmarshaler methods for
|
|
|
|
// ByteDatabase and BasicByteDatabase to use.
|
|
|
|
type ByteCodec interface {
|
|
|
|
Marshal(interface{}) ([]byte, error)
|
|
|
|
Unmarshal([]byte, interface{}) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSONByteCodec is a ByteCodec that uses JSON for marshaling and unmarshaling.
|
|
|
|
var JSONByteCodec = jsonCodec{}
|
|
|
|
|
|
|
|
type jsonCodec struct{}
|
|
|
|
|
|
|
|
func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
|
|
|
|
return json.Marshal(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (jsonCodec) Unmarshal(b []byte, v interface{}) error {
|
|
|
|
return json.Unmarshal(b, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
type byteDatabase interface {
|
|
|
|
// Get gets the given key and returns the byte slice.
|
|
|
|
Get(key string) ([]byte, error)
|
|
|
|
// Set sets the given key and byte slice value into the database.
|
|
|
|
Set(key string, v []byte) error
|
|
|
|
// EachIsOrdered works the same as Database.
|
|
|
|
EachIsOrdered() bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ByteDatabase defines a database that can only store and retrieve bytes. It is
|
|
|
|
// used for wrapping using WrapByteDatabase to form a Database.
|
|
|
|
type ByteDatabase interface {
|
|
|
|
// Bucket should work exactly the same as Database's.
|
|
|
|
Bucket(keys ...string) (ByteDatabase, error)
|
2021-11-17 03:49:22 +00:00
|
|
|
// Get gets the given key and returns the byte slice.
|
|
|
|
Get(key string) ([]byte, error)
|
|
|
|
// Set sets the given key and byte slice value into the database.
|
|
|
|
Set(key string, v []byte) error
|
2021-11-17 03:38:42 +00:00
|
|
|
// Each should behave similarly to Database's, except the bytes value is
|
|
|
|
// passed directly into the callback instead of unmarshaled indirectly.
|
|
|
|
Each(fn func(k string, v []byte) error) error
|
2021-11-17 03:49:22 +00:00
|
|
|
// EachIsOrdered works the same as Database.
|
|
|
|
EachIsOrdered() bool
|
2021-11-17 03:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BasicByteDatabase is the basic variant of ByteDatabase.
|
|
|
|
type BasicByteDatabase interface {
|
2021-11-17 03:49:22 +00:00
|
|
|
// Bucket should work exactly the same as Database's.
|
|
|
|
Bucket(keys ...string) (ByteDatabase, error)
|
|
|
|
// Get gets the given key and returns the byte slice.
|
|
|
|
Get(key string) ([]byte, error)
|
|
|
|
// Set sets the given key and byte slice value into the database.
|
|
|
|
Set(key string, v []byte) error
|
2021-11-17 03:38:42 +00:00
|
|
|
// Each should behave similarly to BasicDatabase's.
|
|
|
|
Each(fn func(k string) error) error
|
2021-11-17 03:49:22 +00:00
|
|
|
// EachIsOrdered works the same as Database.
|
|
|
|
EachIsOrdered() bool
|
2021-11-17 03:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type wrappedByteDatabase struct {
|
|
|
|
wrappedSmallByteDatabase
|
|
|
|
}
|
|
|
|
|
|
|
|
// WrapByteDatabase wraps the given ByteDatabase into a database.
|
|
|
|
func WrapByteDatabase(bytedb ByteDatabase, codec ByteCodec) Database {
|
|
|
|
return wrappedByteDatabase{
|
|
|
|
wrappedSmallByteDatabase: wrapSmallByteDatabase(bytedb, codec),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedByteDatabase) EachIsOrdered() bool {
|
|
|
|
return w.byteDatabase.(ByteDatabase).EachIsOrdered()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedByteDatabase) Bucket(keys ...string) (Database, error) {
|
|
|
|
d, err := w.byteDatabase.(ByteDatabase).Bucket(keys...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
w.byteDatabase = d
|
|
|
|
return w, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedByteDatabase) Each(tmp interface{}, fn func(k string) error) error {
|
|
|
|
db := w.byteDatabase.(ByteDatabase)
|
|
|
|
return db.Each(func(k string, b []byte) error {
|
|
|
|
if err := w.Codec.Unmarshal(b, tmp); err != nil {
|
|
|
|
return errors.Wrap(err, "error unmarshaling ByteDatabase value")
|
|
|
|
}
|
|
|
|
return fn(k)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
type wrappedBasicByteDatabase struct {
|
|
|
|
wrappedSmallByteDatabase
|
|
|
|
}
|
|
|
|
|
|
|
|
// WrapBasicByteDatabase wraps a BasicByteDatabase into a BasicDatabase. The
|
|
|
|
// user should use WrapByteDatabase to wrap the given BasicDatabase into a
|
|
|
|
// Database.
|
|
|
|
func WrapBasicByteDatabase(basic BasicByteDatabase, codec ByteCodec) BasicDatabase {
|
|
|
|
return wrappedBasicByteDatabase{
|
|
|
|
wrappedSmallByteDatabase: wrapSmallByteDatabase(basic, codec),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedBasicByteDatabase) Each(fn func(k string) error) error {
|
|
|
|
return w.byteDatabase.(BasicByteDatabase).Each(fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
type wrappedSmallByteDatabase struct {
|
|
|
|
byteDatabase
|
|
|
|
Codec ByteCodec
|
|
|
|
}
|
|
|
|
|
|
|
|
func wrapSmallByteDatabase(basic byteDatabase, codec ByteCodec) wrappedSmallByteDatabase {
|
|
|
|
return wrappedSmallByteDatabase{
|
|
|
|
byteDatabase: basic,
|
|
|
|
Codec: codec,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedSmallByteDatabase) Get(key string, v interface{}) error {
|
|
|
|
b, err := w.byteDatabase.Get(key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := w.Codec.Unmarshal(b, v); err != nil {
|
|
|
|
return errors.Wrap(err, "error unmarshaling ByteDatabase value")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w wrappedSmallByteDatabase) Set(key string, v interface{}) error {
|
|
|
|
b, err := w.Codec.Marshal(v)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error unmarshaling ByteDatabase value")
|
|
|
|
}
|
|
|
|
if err := w.byteDatabase.Set(key, b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|