cached.go

  1package cache
  2
  3import (
  4	"sync"
  5
  6	"github.com/MichaelMure/git-bug/entities/bug"
  7	"github.com/MichaelMure/git-bug/entities/identity"
  8	"github.com/MichaelMure/git-bug/entity"
  9	"github.com/MichaelMure/git-bug/entity/dag"
 10	"github.com/MichaelMure/git-bug/repository"
 11)
 12
 13// type withSnapshot[SnapT dag.Snapshot, OpT dag.OperationWithApply[SnapT]] struct {
 14// 	dag.Interface[SnapT, OpT]
 15// 	snap dag.Snapshot
 16// }
 17//
 18//
 19// func (ws *withSnapshot[SnapT, OpT]) Compile() dag.Snapshot {
 20// 	if ws.snap == nil {
 21// 		snap := ws.Interface.Compile()
 22// 		ws.snap = snap
 23// 	}
 24// 	return ws.snap
 25// }
 26//
 27// // Append intercept Bug.Append() to update the snapshot efficiently
 28// func (ws *withSnapshot[SnapT, OpT]) Append(op OpT) {
 29// 	ws.Interface.Append(op)
 30//
 31// 	if ws.snap == nil {
 32// 		return
 33// 	}
 34//
 35// 	op.Apply(ws.snap)
 36// 	ws.snap. = append(ws.snap.Operations, op)
 37// }
 38//
 39// // Commit intercept Bug.Commit() to update the snapshot efficiently
 40// func (ws *withSnapshot[SnapT, OpT]) Commit(repo repository.ClockedRepo) error {
 41// 	err := ws.Interface.Commit(repo)
 42//
 43// 	if err != nil {
 44// 		ws.snap = nil
 45// 		return err
 46// 	}
 47//
 48// 	// Commit() shouldn't change anything of the bug state apart from the
 49// 	// initial ID set
 50//
 51// 	if ws.snap == nil {
 52// 		return nil
 53// 	}
 54//
 55// 	ws.snap.id = ws.Interface.Id()
 56// 	return nil
 57// }
 58
 59type CachedEntityBase[SnapT dag.Snapshot, OpT dag.Operation] struct {
 60	entityUpdated   func(id entity.Id) error
 61	getUserIdentity func() (identity.Interface, error)
 62	repo            repository.ClockedRepo
 63
 64	mu     sync.RWMutex
 65	entity dag.Interface[SnapT, OpT]
 66}
 67
 68func (e *CachedEntityBase[SnapT, OpT]) Id() entity.Id {
 69	return e.entity.Id()
 70}
 71
 72func (e *CachedEntityBase[SnapT, OpT]) Snapshot() SnapT {
 73	e.mu.RLock()
 74	defer e.mu.RUnlock()
 75	return e.entity.Compile()
 76}
 77
 78func (e *CachedEntityBase[SnapT, OpT]) notifyUpdated() error {
 79	return e.entityUpdated(e.entity.Id())
 80}
 81
 82// ResolveOperationWithMetadata will find an operation that has the matching metadata
 83func (e *CachedEntityBase[SnapT, OpT]) ResolveOperationWithMetadata(key string, value string) (entity.Id, error) {
 84	e.mu.RLock()
 85	defer e.mu.RUnlock()
 86	// preallocate but empty
 87	matching := make([]entity.Id, 0, 5)
 88
 89	for _, op := range e.entity.Operations() {
 90		opValue, ok := op.GetMetadata(key)
 91		if ok && value == opValue {
 92			matching = append(matching, op.Id())
 93		}
 94	}
 95
 96	if len(matching) == 0 {
 97		return "", ErrNoMatchingOp
 98	}
 99
100	if len(matching) > 1 {
101		return "", bug.NewErrMultipleMatchOp(matching)
102	}
103
104	return matching[0], nil
105}
106
107func (e *CachedEntityBase[SnapT, OpT]) Validate() error {
108	e.mu.RLock()
109	defer e.mu.RUnlock()
110	return e.entity.Validate()
111}
112
113func (e *CachedEntityBase[SnapT, OpT]) Commit() error {
114	e.mu.Lock()
115	err := e.entity.Commit(e.repo)
116	if err != nil {
117		e.mu.Unlock()
118		return err
119	}
120	e.mu.Unlock()
121	return e.notifyUpdated()
122}
123
124func (e *CachedEntityBase[SnapT, OpT]) CommitAsNeeded() error {
125	e.mu.Lock()
126	err := e.entity.CommitAsNeeded(e.repo)
127	if err != nil {
128		e.mu.Unlock()
129		return err
130	}
131	e.mu.Unlock()
132	return e.notifyUpdated()
133}
134
135func (e *CachedEntityBase[SnapT, OpT]) NeedCommit() bool {
136	e.mu.RLock()
137	defer e.mu.RUnlock()
138	return e.entity.NeedCommit()
139}