func (cs *contentStore) safeSave(auth, key string, content []byte, tags ...storage.Tag) error {
	cs.lock.RLock()
	defer cs.lock.RUnlock()
	store, err := cs.open(auth)
	if err != nil {
		return err
	}
	_, err = store.Get(key)
	if errors.Is(err, storage.ErrDataNotFound) {
		return store.Put(key, content, tags...)
	} else if err != nil {
		return err
	}
	return errors.New("content with same type and id already exists in this wallet")
}
safeSave saves given content to store by given key but returns error if content with given key already exists.
package wallet
import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"
	"github.com/bluele/gcache"
	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
	"github.com/hyperledger/aries-framework-go/spi/storage"
)
type ContentType string
const (
	Collection ContentType = "collection"
	Credential ContentType = "credential"
	DIDResolutionResponse ContentType = "didResolutionResponse"
	Metadata ContentType = "metadata"
	Connection ContentType = "connection"
	Key ContentType = "key"
)
func (ct ContentType) IsValid() error {
	switch ct {
	case Collection, Credential, DIDResolutionResponse, Metadata, Connection, Key:
		return nil
	}
	return fmt.Errorf("invalid content type '%s', supported types are %s", ct,
		[]ContentType{Collection, Credential, DIDResolutionResponse, Metadata, Connection, Key})
}
func (ct ContentType) Name() string {
	return string(ct)
}
const (
	collectionMappingKeyPrefix = "collectionmapping"
)
type keyContent struct {
	ID               string          `json:"id"`
	KeyType          string          `json:"type"`
	PrivateKeyJwk    json.RawMessage `json:"privateKeyJwk"`
	PrivateKeyBase58 string          `json:"privateKeyBase58"`
}
type contentID struct {
	ID string `json:"id"`
}
type storeOpenHandle func(string) (storage.Store, error)
type storeCloseHandle func() error
var (
	storeLocked storeOpenHandle = func(string) (storage.Store, error) {
		return nil, ErrWalletLocked
	}
	noOp = func() error { return nil }
)
type contentStore struct {
	storeID  string
	provider *storageProvider
	open     storeOpenHandle
	close    storeCloseHandle
	lock     sync.RWMutex
}
func newContentStore(p storage.Provider, pr *profile) *contentStore {
	contents := &contentStore{open: storeLocked, close: noOp, provider: newWalletStorageProvider(pr, p), storeID: pr.ID}
	if store, err := storeManager().get(pr.ID); err == nil {
		contents.updateStoreHandles(store)
	}
	return contents
}
func (cs *contentStore) Open(auth string, opts *unlockOpts) error {
	store, err := cs.provider.OpenStore(auth, opts, storage.StoreConfiguration{TagNames: []string{
		Collection.Name(), Credential.Name(), Connection.Name(), DIDResolutionResponse.Name(), Connection.Name(), Key.Name(),
	}})
	if err != nil {
		return err
	}
	if err := storeManager().persist(cs.storeID, store, opts.tokenExpiry); err != nil {
		return err
	}
	cs.lock.Lock()
	defer cs.lock.Unlock()
	cs.updateStoreHandles(store)
	return nil
}
func (cs *contentStore) updateStoreHandles(store storage.Store) {
	cs.open = func(auth string) (storage.Store, error) {
		_, err := keyManager().getKeyManger(auth)
		if err != nil {
			return nil, ErrInvalidAuthToken
		}
		return store, nil
	}
	cs.close = func() error {
		return store.Close()
	}
}
func (cs *contentStore) Close() bool {
	cs.lock.Lock()
	defer cs.lock.Unlock()
	if err := cs.close(); err != nil {
		logger.Debugf("failed to close wallet content store: %s", err)
	}
	cs.open = storeLocked
	cs.close = noOp
	return storeManager().delete(cs.storeID)
}
func (cs *contentStore) Save(auth string, ct ContentType, content []byte, options ...AddContentOptions) error {
	opts := &addContentOpts{}
	for _, option := range options {
		option(opts)
	}
	switch ct {
	case Collection, Metadata, Connection, Credential:
		key, err := getContentID(content)
		if err != nil {
			return err
		}
		err = cs.mapCollection(auth, key, opts.collectionID, ct)
		if err != nil {
			return err
		}
		return cs.safeSave(auth, getContentKeyPrefix(ct, key), content, storage.Tag{Name: ct.Name()})
	case DIDResolutionResponse:
		docRes, err := did.ParseDocumentResolution(content)
		if err != nil {
			return fmt.Errorf("invalid DID resolution response model: %w", err)
		}
		err = cs.mapCollection(auth, docRes.DIDDocument.ID, opts.collectionID, ct)
		if err != nil {
			return err
		}
		return cs.safeSave(auth, getContentKeyPrefix(ct, docRes.DIDDocument.ID), content, storage.Tag{Name: ct.Name()})
	case Key:
		var key keyContent
		err := json.Unmarshal(content, &key)
		if err != nil {
			return fmt.Errorf("failed to read key contents: %w", err)
		}
		return saveKey(auth, &key)
	default:
		return fmt.Errorf("invalid content type '%s', supported types are %s", ct,
			[]ContentType{Collection, Credential, DIDResolutionResponse, Metadata, Connection, Key})
	}
}