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
12var _ entity.Interface[entity.Snapshot, dag.Operation] = &CachedEntityBase[entity.Snapshot, dag.Operation]{}
13var _ CacheEntity = &CachedEntityBase[entity.Snapshot, dag.Operation]{}
14
15// CachedEntityBase provide the base function of an entity managed by the cache.
16type CachedEntityBase[SnapT entity.Snapshot, OpT dag.Operation] struct {
17 repo repository.ClockedRepo
18 entityUpdated func(id entity.Id) error
19 getUserIdentity getUserIdentityFunc
20
21 mu sync.RWMutex
22 entity entity.WithCommit[SnapT, OpT]
23}
24
25func (e *CachedEntityBase[SnapT, OpT]) Id() entity.Id {
26 return e.entity.Id()
27}
28
29func (e *CachedEntityBase[SnapT, OpT]) Compile() SnapT {
30 e.mu.RLock()
31 defer e.mu.RUnlock()
32 return e.entity.Compile()
33}
34
35func (e *CachedEntityBase[SnapT, OpT]) notifyUpdated() error {
36 return e.entityUpdated(e.entity.Id())
37}
38
39// ResolveOperationWithMetadata will find an operation that has the matching metadata
40func (e *CachedEntityBase[SnapT, OpT]) ResolveOperationWithMetadata(key string, value string) (entity.Id, error) {
41 e.mu.RLock()
42 defer e.mu.RUnlock()
43 // preallocate but empty
44 matching := make([]entity.Id, 0, 5)
45
46 for _, op := range e.entity.Operations() {
47 opValue, ok := op.GetMetadata(key)
48 if ok && value == opValue {
49 matching = append(matching, op.Id())
50 }
51 }
52
53 if len(matching) == 0 {
54 return "", ErrNoMatchingOp
55 }
56
57 if len(matching) > 1 {
58 return "", entity.NewErrMultipleMatch("operation", matching)
59 }
60
61 return matching[0], nil
62}
63
64func (e *CachedEntityBase[SnapT, OpT]) Validate() error {
65 e.mu.RLock()
66 defer e.mu.RUnlock()
67 return e.entity.Validate()
68}
69
70func (e *CachedEntityBase[SnapT, OpT]) Append(op OpT) {
71 e.mu.Lock()
72 defer e.mu.Unlock()
73 e.entity.Append(op)
74}
75
76func (e *CachedEntityBase[SnapT, OpT]) Operations() []OpT {
77 e.mu.RLock()
78 defer e.mu.RUnlock()
79 return e.entity.Operations()
80}
81
82func (e *CachedEntityBase[SnapT, OpT]) Commit() error {
83 e.mu.Lock()
84 err := e.entity.Commit(e.repo)
85 if err != nil {
86 e.mu.Unlock()
87 return err
88 }
89 e.mu.Unlock()
90 return e.notifyUpdated()
91}
92
93func (e *CachedEntityBase[SnapT, OpT]) CommitAsNeeded() error {
94 e.mu.Lock()
95 err := e.entity.CommitAsNeeded(e.repo)
96 if err != nil {
97 e.mu.Unlock()
98 return err
99 }
100 e.mu.Unlock()
101 return e.notifyUpdated()
102}
103
104func (e *CachedEntityBase[SnapT, OpT]) NeedCommit() bool {
105 e.mu.RLock()
106 defer e.mu.RUnlock()
107 return e.entity.NeedCommit()
108}
109
110func (e *CachedEntityBase[SnapT, OpT]) Lock() {
111 e.mu.Lock()
112}
113
114func (e *CachedEntityBase[SnapT, OpT]) CreateLamportTime() lamport.Time {
115 return e.entity.CreateLamportTime()
116}
117
118func (e *CachedEntityBase[SnapT, OpT]) EditLamportTime() lamport.Time {
119 return e.entity.EditLamportTime()
120}
121
122func (e *CachedEntityBase[SnapT, OpT]) FirstOp() OpT {
123 return e.entity.FirstOp()
124}