func (db *DB) Bucket(name string) *Bucket {
	bucket, ok := db.bucket(name)
	if !ok {
		if db.readOnly {
			return &Bucket{err: ErrNotFound}
		}
		bucket, err := db.createBucket(nil, name)
		if err != nil {
			return &Bucket{err: err}
		}
		return bucket
	}
	return bucket
}
Bucket returns the named bucket, creating it if it doesn't exist.
If an error has occurred during creation, it would be returned by
any operation on the returned bucket.
package bow
import (
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"math/rand"
	"sync"
	"github.com/dgraph-io/badger/v2"
	"github.com/zippoxer/bow/codec"
	jsoncodec "github.com/zippoxer/bow/codec/json"
	keycodec "github.com/zippoxer/bow/codec/key"
	"github.com/sony/sonyflake"
)
var (
	ErrNotFound = errors.New("Record doesn't exist")
	ErrReadOnly = errors.New("Put and Delete aren't allowed in read-only mode")
)
const version = 1
const bucketIdSize = 2
const MaxBuckets = math.MaxUint16 - (8 * 256)
const reserved byte = 0x00
var (
	bucketIdSequence = []byte{reserved, 0x00}
	metaKey = []byte{reserved, 0x01}
)
var (
	keyCodec = keycodec.Codec{}
	sonyflakeKeygen = sonyflake.NewSonyflake(sonyflake.Settings{
		MachineID: func() (uint16, error) {
			return uint16(rand.Uint32() & (1<<16 - 1)), nil
		},
	})
)
type Id string
func NewId() Id {
	id, err := sonyflakeKeygen.NextID()
	if err != nil {
		panic(fmt.Sprintf("bow.NewId: %v", err))
	}
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, id)
	return Id(b)
}
func ParseId(s string) (Id, error) {
	b, err := base64.RawURLEncoding.DecodeString(s)
	if err != nil {
		return "", err
	}
	if len(b) != 8 {
		return "", fmt.Errorf("bow.ParseId: input must be exactly 8 bytes long")
	}
	return Id(b), nil
}
func (id Id) String() string {
	return base64.RawURLEncoding.EncodeToString([]byte(id))
}
func (id Id) MarshalJSON() ([]byte, error) {
	s := id.String()
	b := make([]byte, len(s)+2)
	b[0] = '"'
	copy(b[1:], s)
	b[len(b)-1] = '"'
	return b, nil
}
func (id *Id) UnmarshalJSON(data []byte) error {
	var s string
	err := json.Unmarshal(data, &s)
	if err != nil {
		return err
	}
	if s == "" {
		return nil
	}
	*id, err = ParseId(s)
	return err
}
func (id Id) Marshal(in []byte) ([]byte, error) {
	return []byte(id), nil
}
func (id *Id) Unmarshal(b []byte) error {
	*id = Id(b)
	return nil
}
type Option func(db *DB) error
func SetReadOnly(readOnly bool) Option {
	return func(db *DB) error {
		db.readOnly = readOnly
		return nil
	}
}
func SetCodec(c codec.Codec) Option {
	return func(db *DB) error {
		db.codec = c
		return nil
	}
}
func SetLogger(logger badger.Logger) Option {
	return func(db *DB) error {
		db.badgerOptions.Logger = logger
		return nil
	}
}
func SetBadgerOptions(o badger.Options) Option {
	return func(db *DB) error {
		db.badgerOptions = o
		return nil
	}
}
type DB struct {
	db       *badger.DB
	meta     meta
	metaMu   sync.RWMutex
	bucketId *badger.Sequence
	readOnly      bool
	codec         codec.Codec
	badgerOptions badger.Options
}
func Open(dir string, options ...Option) (*DB, error) {
	db := &DB{
		badgerOptions: badger.DefaultOptions(dir),
		codec:         jsoncodec.Codec{},
	}
	for _, option := range options {
		err := option(db)
		if err != nil {
			return nil, err
		}
	}
	if db.readOnly || db.badgerOptions.ReadOnly {
		db.readOnly = true
		db.badgerOptions.ReadOnly = true
	}
	if db.badgerOptions.Dir == "" {
		db.badgerOptions.Dir = dir
	}
	if db.badgerOptions.ValueDir == "" {
		db.badgerOptions.ValueDir = dir
	}
	bdb, err := badger.Open(db.badgerOptions)
	if err != nil {
		return nil, err
	}
	db.db = bdb
	err = db.readMeta(nil)
	if err == badger.ErrKeyNotFound {
		db.meta = meta{
			Version: version,
			Buckets: make(map[string]bucketMeta),
		}
		if !db.readOnly {
			err = db.writeMeta(nil)
			if err != nil {
				return nil, err
			}
		}
	} else if err != nil {
		return nil, err
	}
	if !db.readOnly {
		db.bucketId, err = db.db.GetSequence(bucketIdSequence, 1e3)
		if err != nil {
			return nil, err
		}
	}
	return db, nil
}