func (s *store) Put(key string, value []byte, tags ...storage.Tag) error {
	if key == "" {
		return errors.New("key cannot be blank")
	}
	if value == nil {
		return errors.New("value cannot be nil")
	}
	for _, tag := range tags {
		if strings.Contains(tag.Name, ":") {
			return fmt.Errorf(invalidTagName, tag.Name)
		}
		if strings.Contains(tag.Value, ":") {
			return fmt.Errorf(invalidTagValue, tag.Value)
		}
	}
	var newDBEntry dbEntry
	newDBEntry.Value = value
	if len(tags) > 0 {
		newDBEntry.Tags = tags
		err := s.updateTagMap(key, tags)
		if err != nil {
			return fmt.Errorf("failed to update tag map: %w", err)
		}
	}
	entryBytes, err := json.Marshal(newDBEntry)
	if err != nil {
		return fmt.Errorf("failed to marshal new DB entry: %w", err)
	}
	return s.db.Put([]byte(key), entryBytes, nil)
}
Put stores the key and the record.
WARNING: When storing tags, a race condition can occur if this is called from two different store objects
that point to the same underlying database at the same time, causing the tag map to be incorrect. You will need
to add locks.
TODO (#2947) This current implementation doesn't update the tag map if tags is empty, but this isn't correct.
An empty tags slice should remove any stored tags for this key-value pair.
package leveldb
import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/hyperledger/aries-framework-go/spi/storage"
)
const (
	pathPattern = "%s-%s"
	invalidTagName                  = `"%s" is an invalid tag name since it contains one or more ':' characters`
	invalidTagValue                 = `"%s" is an invalid tag value since it contains one or more ':' characters`
	tagMapKey                       = "TagMap"
	storeConfigKey                  = "StoreConfig"
	expressionTagNameOnlyLength     = 1
	expressionTagNameAndValueLength = 2
	invalidQueryExpressionFormat    = `"%s" is not in a valid expression format. ` +
		"it must be in the following format: TagName:TagValue"
)
type Provider struct {
	dbPath string
	dbs    map[string]*store
	lock   sync.RWMutex
}
type closer func(storeName string)
type tagMapping map[string]map[string]struct{}  
type dbEntry struct {
	Value []byte        `json:"value,omitempty"`
	Tags  []storage.Tag `json:"tags,omitempty"`
}
func NewProvider(dbPath string) *Provider {
	return &Provider{dbs: make(map[string]*store), dbPath: dbPath}
}
func (p *Provider) OpenStore(name string) (storage.Store, error) {
	if name == "" {
		return nil, errors.New("store name cannot be blank")
	}
	name = strings.ToLower(name)
	store := p.getLeveldbStore(name)
	if store == nil {
		return p.newLeveldbStore(name)
	}
	return store, nil
}
func (p *Provider) SetStoreConfig(name string, config storage.StoreConfiguration) error {
	for _, tagName := range config.TagNames {
		if strings.Contains(tagName, ":") {
			return fmt.Errorf(invalidTagName, tagName)
		}
	}
	name = strings.ToLower(name)
	openStore, ok := p.dbs[name]
	if !ok {
		return storage.ErrStoreNotFound
	}
	configBytes, err := json.Marshal(config)
	if err != nil {
		return fmt.Errorf("failed to marshal store configuration: %w", err)
	}
	err = openStore.Put(storeConfigKey, configBytes)
	if err != nil {
		return fmt.Errorf("failed to put store store configuration: %w", err)
	}
	return nil
}
func (p *Provider) GetStoreConfig(name string) (storage.StoreConfiguration, error) {
	name = strings.ToLower(name)
	openStore, ok := p.dbs[name]
	if !ok {
		return storage.StoreConfiguration{}, storage.ErrStoreNotFound
	}
	storeConfigBytes, err := openStore.Get(storeConfigKey)
	if err != nil {
		return storage.StoreConfiguration{},
			fmt.Errorf(`failed to get store configuration for "%s": %w`, name, err)
	}
	var storeConfig storage.StoreConfiguration
	err = json.Unmarshal(storeConfigBytes, &storeConfig)
	if err != nil {
		return storage.StoreConfiguration{}, fmt.Errorf("failed to unmarshal store configuration: %w", err)
	}
	return storeConfig, nil
}
func (p *Provider) GetOpenStores() []storage.Store {
	p.lock.RLock()
	defer p.lock.RUnlock()
	openStores := make([]storage.Store, len(p.dbs))
	var counter int
	for _, db := range p.dbs {
		openStores[counter] = db
		counter++
	}
	return openStores
}
func (p *Provider) Close() error {
	p.lock.RLock()
	openStoresSnapshot := make([]*store, len(p.dbs))
	var counter int
	for _, openStore := range p.dbs {
		openStoresSnapshot[counter] = openStore
		counter++
	}
	p.lock.RUnlock()
	for _, openStore := range openStoresSnapshot {
		err := openStore.Close()
		if err != nil {
			return fmt.Errorf(`failed to close open store with name "%s": %w`, openStore.name, err)
		}
	}
	return nil
}
func (p *Provider) getLeveldbStore(name string) *store {
	p.lock.RLock()
	defer p.lock.RUnlock()
	return p.dbs[name]
}
func (p *Provider) newLeveldbStore(name string) (*store, error) {
	p.lock.Lock()
	defer p.lock.Unlock()
	db, err := leveldb.OpenFile(fmt.Sprintf(pathPattern, p.dbPath, name), nil)
	if err != nil {
		return nil, err
	}
	store := &store{db: db, name: name, close: p.removeStore}
	p.dbs[name] = store
	return store, nil
}
func (p *Provider) removeStore(name string) {
	p.lock.Lock()
	defer p.lock.Unlock()
	_, ok := p.dbs[name]
	if ok {
		delete(p.dbs, name)
	}
}
type store struct {
	db    *leveldb.DB
	name  string
	close closer
	lock  sync.RWMutex
}