1package cache
2
3import (
4 "sync"
5
6 "github.com/git-bug/git-bug/entity"
7 "github.com/git-bug/git-bug/entity/dag"
8 "github.com/git-bug/git-bug/repository"
9 "github.com/git-bug/git-bug/util/lamport"
10)
11
12var _ dag.ReadOnly[dag.Snapshot, dag.Operation] = &CachedEntityBase[dag.Snapshot, dag.Operation]{}
13var _ CacheEntity = &CachedEntityBase[dag.Snapshot, dag.Operation]{}
14
15// CachedEntityBase provide the base function of an entity managed by the cache.
16type CachedEntityBase[SnapT dag.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 dag.ReadWrite[SnapT, OpT]
23}
24
25func (e *CachedEntityBase[SnapT, OpT]) Id() entity.Id {
26 return e.entity.Id()
27}
28
29func (e *CachedEntityBase[SnapT, OpT]) Snapshot() SnapT {
30 e.mu.RLock()
31 defer e.mu.RUnlock()
32 return e.entity.Snapshot()
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]) Commit() error {
71 e.mu.Lock()
72 err := e.entity.Commit(e.repo)
73 if err != nil {
74 e.mu.Unlock()
75 return err
76 }
77 e.mu.Unlock()
78 return e.notifyUpdated()
79}
80
81func (e *CachedEntityBase[SnapT, OpT]) CommitAsNeeded() error {
82 e.mu.Lock()
83 err := e.entity.CommitAsNeeded(e.repo)
84 if err != nil {
85 e.mu.Unlock()
86 return err
87 }
88 e.mu.Unlock()
89 return e.notifyUpdated()
90}
91
92func (e *CachedEntityBase[SnapT, OpT]) NeedCommit() bool {
93 e.mu.RLock()
94 defer e.mu.RUnlock()
95 return e.entity.NeedCommit()
96}
97
98func (e *CachedEntityBase[SnapT, OpT]) Lock() {
99 e.mu.Lock()
100}
101
102func (e *CachedEntityBase[SnapT, OpT]) CreateLamportTime() lamport.Time {
103 return e.entity.CreateLamportTime()
104}
105
106func (e *CachedEntityBase[SnapT, OpT]) EditLamportTime() lamport.Time {
107 return e.entity.EditLamportTime()
108}
109
110func (e *CachedEntityBase[SnapT, OpT]) FirstOp() OpT {
111 return e.entity.FirstOp()
112}
113
114func (e *CachedEntityBase[SnapT, OpT]) LastOp() OpT {
115 return e.entity.LastOp()
116}