181 lines
3.7 KiB
Go
181 lines
3.7 KiB
Go
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"github.com/akrylysov/pogreb"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
|
|
"github.com/gogo/protobuf/proto"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type treeStorage struct {
|
|
db *pogreb.DB
|
|
path treeKeys
|
|
id string
|
|
rootPath []byte
|
|
headsPath []byte
|
|
heads []string
|
|
root *treechangeproto.RawTreeChangeWithId
|
|
headsMx sync.Mutex
|
|
}
|
|
|
|
func newTreeStorage(db *pogreb.DB, treeId string) (ts storage.TreeStorage, err error) {
|
|
path := treeKeys{treeId}
|
|
heads, err := db.Get([]byte(path.HeadsKey()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if heads == nil {
|
|
err = storage.ErrUnknownTreeId
|
|
return
|
|
}
|
|
|
|
res, err := db.Get([]byte(path.RootKey()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if res == nil {
|
|
err = storage.ErrUnknownTreeId
|
|
return
|
|
}
|
|
|
|
root := &treechangeproto.RawTreeChangeWithId{}
|
|
err = proto.Unmarshal(res, root)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ts = &treeStorage{
|
|
db: db,
|
|
path: path,
|
|
rootPath: []byte(path.RootKey()),
|
|
headsPath: []byte(path.HeadsKey()),
|
|
id: treeId,
|
|
heads: parseHeads(heads),
|
|
root: root,
|
|
}
|
|
return
|
|
}
|
|
|
|
func createTreeStorage(db *pogreb.DB, payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
|
|
keys := treeKeys{id: payload.TreeId}
|
|
has, err := db.Has([]byte(keys.RootKey()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !has {
|
|
err = storage.ErrUnknownTreeId
|
|
return
|
|
}
|
|
|
|
heads := createHeadsPayload(payload.Heads)
|
|
|
|
for _, ch := range payload.Changes {
|
|
err = db.Put([]byte(keys.RawChangeKey(ch.Id)), ch.GetRawChange())
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
err = db.Put([]byte(keys.HeadsKey()), heads)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// duplicating same change in raw changes
|
|
err = db.Put([]byte(keys.RawChangeKey(payload.TreeId)), payload.RootRawChange.GetRawChange())
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = db.Put([]byte(keys.RootKey()), payload.RootRawChange.GetRawChange())
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ts = &treeStorage{
|
|
db: db,
|
|
path: keys,
|
|
rootPath: []byte(keys.RootKey()),
|
|
headsPath: []byte(keys.HeadsKey()),
|
|
id: payload.TreeId,
|
|
heads: payload.Heads,
|
|
root: payload.RootRawChange,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (t *treeStorage) ID() (string, error) {
|
|
return t.id, nil
|
|
}
|
|
|
|
func (t *treeStorage) Root() (raw *treechangeproto.RawTreeChangeWithId, err error) {
|
|
return t.root, nil
|
|
}
|
|
|
|
func (t *treeStorage) Heads() ([]string, error) {
|
|
t.headsMx.Lock()
|
|
defer t.headsMx.Unlock()
|
|
return t.heads, nil
|
|
}
|
|
|
|
func (t *treeStorage) SetHeads(heads []string) (err error) {
|
|
defer func() {
|
|
if err == nil {
|
|
t.headsMx.Lock()
|
|
t.heads = heads
|
|
t.headsMx.Unlock()
|
|
}
|
|
}()
|
|
payload := createHeadsPayload(heads)
|
|
return t.db.Put(t.headsPath, payload)
|
|
}
|
|
|
|
func (t *treeStorage) AddRawChange(change *treechangeproto.RawTreeChangeWithId) (err error) {
|
|
return t.db.Put([]byte(change.Id), change.RawChange)
|
|
}
|
|
|
|
func (t *treeStorage) GetRawChange(ctx context.Context, id string) (raw *treechangeproto.RawTreeChangeWithId, err error) {
|
|
res, err := t.db.Get([]byte(t.path.RawChangeKey(id)))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
raw = &treechangeproto.RawTreeChangeWithId{
|
|
RawChange: res,
|
|
Id: id,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (t *treeStorage) HasChange(ctx context.Context, id string) (bool, error) {
|
|
return t.db.Has([]byte(id))
|
|
}
|
|
|
|
func parseHeads(headsPayload []byte) []string {
|
|
return strings.Split(string(headsPayload), "/")
|
|
}
|
|
|
|
func createHeadsPayload(heads []string) []byte {
|
|
var (
|
|
b bytes.Buffer
|
|
totalLen int
|
|
)
|
|
for _, s := range heads {
|
|
totalLen += len(s)
|
|
}
|
|
// adding separators
|
|
totalLen += len(heads) - 1
|
|
b.Grow(totalLen)
|
|
for idx, s := range heads {
|
|
if idx > 0 {
|
|
b.WriteString("/")
|
|
}
|
|
b.WriteString(s)
|
|
}
|
|
return b.Bytes()
|
|
}
|