summaryrefslogtreecommitdiffstatshomepage
path: root/cache/cached.go
blob: d2531f0f2d391d54b657dd1889d5f423a91f7165 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package cache

import (
	"sync"

	"github.com/MichaelMure/git-bug/entity"
	"github.com/MichaelMure/git-bug/entity/dag"
	"github.com/MichaelMure/git-bug/repository"
	"github.com/MichaelMure/git-bug/util/lamport"
)

var _ entity.Interface[entity.Snapshot, dag.Operation] = &CachedEntityBase[entity.Snapshot, dag.Operation]{}
var _ CacheEntity = &CachedEntityBase[entity.Snapshot, dag.Operation]{}

// CachedEntityBase provide the base function of an entity managed by the cache.
type CachedEntityBase[SnapT entity.Snapshot, OpT dag.Operation] struct {
	repo            repository.ClockedRepo
	entityUpdated   func(id entity.Id) error
	getUserIdentity getUserIdentityFunc

	mu     sync.RWMutex
	entity entity.WithCommit[SnapT, OpT]
}

func (e *CachedEntityBase[SnapT, OpT]) Id() entity.Id {
	return e.entity.Id()
}

func (e *CachedEntityBase[SnapT, OpT]) Compile() SnapT {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.entity.Compile()
}

func (e *CachedEntityBase[SnapT, OpT]) notifyUpdated() error {
	return e.entityUpdated(e.entity.Id())
}

// ResolveOperationWithMetadata will find an operation that has the matching metadata
func (e *CachedEntityBase[SnapT, OpT]) ResolveOperationWithMetadata(key string, value string) (entity.Id, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()
	// preallocate but empty
	matching := make([]entity.Id, 0, 5)

	for _, op := range e.entity.Operations() {
		opValue, ok := op.GetMetadata(key)
		if ok && value == opValue {
			matching = append(matching, op.Id())
		}
	}

	if len(matching) == 0 {
		return "", ErrNoMatchingOp
	}

	if len(matching) > 1 {
		return "", entity.NewErrMultipleMatch("operation", matching)
	}

	return matching[0], nil
}

func (e *CachedEntityBase[SnapT, OpT]) Validate() error {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.entity.Validate()
}

func (e *CachedEntityBase[SnapT, OpT]) Append(op OpT) {
	e.mu.Lock()
	defer e.mu.Unlock()
	e.entity.Append(op)
}

func (e *CachedEntityBase[SnapT, OpT]) Operations() []OpT {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.entity.Operations()
}

func (e *CachedEntityBase[SnapT, OpT]) Commit() error {
	e.mu.Lock()
	err := e.entity.Commit(e.repo)
	if err != nil {
		e.mu.Unlock()
		return err
	}
	e.mu.Unlock()
	return e.notifyUpdated()
}

func (e *CachedEntityBase[SnapT, OpT]) CommitAsNeeded() error {
	e.mu.Lock()
	err := e.entity.CommitAsNeeded(e.repo)
	if err != nil {
		e.mu.Unlock()
		return err
	}
	e.mu.Unlock()
	return e.notifyUpdated()
}

func (e *CachedEntityBase[SnapT, OpT]) NeedCommit() bool {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.entity.NeedCommit()
}

func (e *CachedEntityBase[SnapT, OpT]) Lock() {
	e.mu.Lock()
}

func (e *CachedEntityBase[SnapT, OpT]) CreateLamportTime() lamport.Time {
	return e.entity.CreateLamportTime()
}

func (e *CachedEntityBase[SnapT, OpT]) EditLamportTime() lamport.Time {
	return e.entity.EditLamportTime()
}

func (e *CachedEntityBase[SnapT, OpT]) FirstOp() OpT {
	return e.entity.FirstOp()
}