1package cache
2
3import (
4 "fmt"
5 "time"
6
7 "github.com/MichaelMure/git-bug/entities/bug"
8 "github.com/MichaelMure/git-bug/entity"
9 "github.com/MichaelMure/git-bug/entity/dag"
10 "github.com/MichaelMure/git-bug/repository"
11)
12
13var ErrNoMatchingOp = fmt.Errorf("no matching operation found")
14
15var _ bug.Interface = &BugCache{}
16
17// BugCache is a wrapper around a Bug. It provides multiple functions:
18//
19// 1. Provide a higher level API to use than the raw API from Bug.
20// 2. Maintain an up-to-date Snapshot available.
21// 3. Deal with concurrency.
22type BugCache struct {
23 CachedEntityBase[*bug.Snapshot, bug.Operation]
24}
25
26func NewBugCache(b *bug.Bug, repo repository.ClockedRepo, getUserIdentity getUserIdentityFunc, entityUpdated func(id entity.Id) error) *BugCache {
27 return &BugCache{
28 CachedEntityBase: CachedEntityBase[*bug.Snapshot, bug.Operation]{
29 repo: repo,
30 entityUpdated: entityUpdated,
31 getUserIdentity: getUserIdentity,
32 entity: &withSnapshot[*bug.Snapshot, bug.Operation]{WithCommit: b},
33 },
34 }
35}
36
37func (c *BugCache) AddComment(message string) (entity.CombinedId, *bug.AddCommentOperation, error) {
38 return c.AddCommentWithFiles(message, nil)
39}
40
41func (c *BugCache) AddCommentWithFiles(message string, files []repository.Hash) (entity.CombinedId, *bug.AddCommentOperation, error) {
42 author, err := c.getUserIdentity()
43 if err != nil {
44 return entity.UnsetCombinedId, nil, err
45 }
46
47 return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil)
48}
49
50func (c *BugCache) AddCommentRaw(author entity.Identity, unixTime int64, message string, files []repository.Hash, metadata map[string]string) (entity.CombinedId, *bug.AddCommentOperation, error) {
51 c.mu.Lock()
52 commentId, op, err := bug.AddComment(c.entity, author, unixTime, message, files, metadata)
53 c.mu.Unlock()
54 if err != nil {
55 return entity.UnsetCombinedId, nil, err
56 }
57 return commentId, op, c.notifyUpdated()
58}
59
60func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, *bug.LabelChangeOperation, error) {
61 author, err := c.getUserIdentity()
62 if err != nil {
63 return nil, nil, err
64 }
65
66 return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil)
67}
68
69func (c *BugCache) ChangeLabelsRaw(author entity.Identity, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, *bug.LabelChangeOperation, error) {
70 c.mu.Lock()
71 changes, op, err := bug.ChangeLabels(c.entity, author, unixTime, added, removed, metadata)
72 c.mu.Unlock()
73 if err != nil {
74 return changes, nil, err
75 }
76 return changes, op, c.notifyUpdated()
77}
78
79func (c *BugCache) ForceChangeLabels(added []string, removed []string) (*bug.LabelChangeOperation, error) {
80 author, err := c.getUserIdentity()
81 if err != nil {
82 return nil, err
83 }
84
85 return c.ForceChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil)
86}
87
88func (c *BugCache) ForceChangeLabelsRaw(author entity.Identity, unixTime int64, added []string, removed []string, metadata map[string]string) (*bug.LabelChangeOperation, error) {
89 c.mu.Lock()
90 op, err := bug.ForceChangeLabels(c.entity, author, unixTime, added, removed, metadata)
91 c.mu.Unlock()
92 if err != nil {
93 return nil, err
94 }
95 return op, c.notifyUpdated()
96}
97
98func (c *BugCache) Open() (*bug.SetStatusOperation, error) {
99 author, err := c.getUserIdentity()
100 if err != nil {
101 return nil, err
102 }
103
104 return c.OpenRaw(author, time.Now().Unix(), nil)
105}
106
107func (c *BugCache) OpenRaw(author entity.Identity, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) {
108 c.mu.Lock()
109 op, err := bug.Open(c.entity, author, unixTime, metadata)
110 c.mu.Unlock()
111 if err != nil {
112 return nil, err
113 }
114 return op, c.notifyUpdated()
115}
116
117func (c *BugCache) Close() (*bug.SetStatusOperation, error) {
118 author, err := c.getUserIdentity()
119 if err != nil {
120 return nil, err
121 }
122
123 return c.CloseRaw(author, time.Now().Unix(), nil)
124}
125
126func (c *BugCache) CloseRaw(author entity.Identity, unixTime int64, metadata map[string]string) (*bug.SetStatusOperation, error) {
127 c.mu.Lock()
128 op, err := bug.Close(c.entity, author, unixTime, metadata)
129 c.mu.Unlock()
130 if err != nil {
131 return nil, err
132 }
133 return op, c.notifyUpdated()
134}
135
136func (c *BugCache) SetTitle(title string) (*bug.SetTitleOperation, error) {
137 author, err := c.getUserIdentity()
138 if err != nil {
139 return nil, err
140 }
141
142 return c.SetTitleRaw(author, time.Now().Unix(), title, nil)
143}
144
145func (c *BugCache) SetTitleRaw(author entity.Identity, unixTime int64, title string, metadata map[string]string) (*bug.SetTitleOperation, error) {
146 c.mu.Lock()
147 op, err := bug.SetTitle(c.entity, author, unixTime, title, metadata)
148 c.mu.Unlock()
149 if err != nil {
150 return nil, err
151 }
152 return op, c.notifyUpdated()
153}
154
155// EditCreateComment is a convenience function to edit the body of a bug (the first comment)
156func (c *BugCache) EditCreateComment(body string) (entity.CombinedId, *bug.EditCommentOperation, error) {
157 author, err := c.getUserIdentity()
158 if err != nil {
159 return entity.UnsetCombinedId, nil, err
160 }
161
162 return c.EditCreateCommentRaw(author, time.Now().Unix(), body, nil)
163}
164
165// EditCreateCommentRaw is a convenience function to edit the body of a bug (the first comment)
166func (c *BugCache) EditCreateCommentRaw(author entity.Identity, unixTime int64, body string, metadata map[string]string) (entity.CombinedId, *bug.EditCommentOperation, error) {
167 c.mu.Lock()
168 commentId, op, err := bug.EditCreateComment(c.entity, author, unixTime, body, nil, metadata)
169 c.mu.Unlock()
170 if err != nil {
171 return entity.UnsetCombinedId, nil, err
172 }
173 return commentId, op, c.notifyUpdated()
174}
175
176func (c *BugCache) EditComment(target entity.CombinedId, message string) (*bug.EditCommentOperation, error) {
177 author, err := c.getUserIdentity()
178 if err != nil {
179 return nil, err
180 }
181
182 return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil)
183}
184
185func (c *BugCache) EditCommentRaw(author entity.Identity, unixTime int64, target entity.CombinedId, message string, metadata map[string]string) (*bug.EditCommentOperation, error) {
186 comment, err := c.Compile().SearchComment(target)
187 if err != nil {
188 return nil, err
189 }
190
191 c.mu.Lock()
192 commentId, op, err := bug.EditComment(c.entity, author, unixTime, comment.TargetId(), message, nil, metadata)
193 c.mu.Unlock()
194 if err != nil {
195 return nil, err
196 }
197 if commentId != target {
198 panic("EditComment returned unexpected comment id")
199 }
200 return op, c.notifyUpdated()
201}
202
203func (c *BugCache) SetMetadata(target entity.Id, newMetadata map[string]string) (*dag.SetMetadataOperation[*bug.Snapshot], error) {
204 author, err := c.getUserIdentity()
205 if err != nil {
206 return nil, err
207 }
208
209 return c.SetMetadataRaw(author, time.Now().Unix(), target, newMetadata)
210}
211
212func (c *BugCache) SetMetadataRaw(author entity.Identity, unixTime int64, target entity.Id, newMetadata map[string]string) (*dag.SetMetadataOperation[*bug.Snapshot], error) {
213 c.mu.Lock()
214 op, err := bug.SetMetadata(c.entity, author, unixTime, target, newMetadata)
215 c.mu.Unlock()
216 if err != nil {
217 return nil, err
218 }
219 return op, c.notifyUpdated()
220}