cached.go

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