1package cache
2
3import (
4 "fmt"
5 "strings"
6 "time"
7
8 "github.com/MichaelMure/git-bug/identity"
9
10 "github.com/MichaelMure/git-bug/bug"
11 "github.com/MichaelMure/git-bug/util/git"
12)
13
14type BugCache struct {
15 repoCache *RepoCache
16 bug *bug.WithSnapshot
17}
18
19func NewBugCache(repoCache *RepoCache, b *bug.Bug) *BugCache {
20 return &BugCache{
21 repoCache: repoCache,
22 bug: &bug.WithSnapshot{Bug: b},
23 }
24}
25
26func (c *BugCache) Snapshot() *bug.Snapshot {
27 return c.bug.Snapshot()
28}
29
30func (c *BugCache) Id() string {
31 return c.bug.Id()
32}
33
34func (c *BugCache) HumanId() string {
35 return c.bug.HumanId()
36}
37
38func (c *BugCache) notifyUpdated() error {
39 return c.repoCache.bugUpdated(c.bug.Id())
40}
41
42var ErrNoMatchingOp = fmt.Errorf("no matching operation found")
43
44type ErrMultipleMatchOp struct {
45 Matching []git.Hash
46}
47
48func (e ErrMultipleMatchOp) Error() string {
49 casted := make([]string, len(e.Matching))
50
51 for i := range e.Matching {
52 casted[i] = string(e.Matching[i])
53 }
54
55 return fmt.Sprintf("Multiple matching operation found:\n%s", strings.Join(casted, "\n"))
56}
57
58// ResolveTargetWithMetadata will find an operation that has the matching metadata
59func (c *BugCache) ResolveTargetWithMetadata(key string, value string) (git.Hash, error) {
60 // preallocate but empty
61 matching := make([]git.Hash, 0, 5)
62
63 it := bug.NewOperationIterator(c.bug)
64 for it.Next() {
65 op := it.Value()
66 opValue, ok := op.GetMetadata(key)
67 if ok && value == opValue {
68 h, err := op.Hash()
69 if err != nil {
70 return "", err
71 }
72 matching = append(matching, h)
73 }
74 }
75
76 if len(matching) == 0 {
77 return "", ErrNoMatchingOp
78 }
79
80 if len(matching) > 1 {
81 return "", ErrMultipleMatchOp{Matching: matching}
82 }
83
84 return matching[0], nil
85}
86
87func (c *BugCache) AddComment(message string) error {
88 return c.AddCommentWithFiles(message, nil)
89}
90
91func (c *BugCache) AddCommentWithFiles(message string, files []git.Hash) error {
92 author, err := identity.GetIdentity(c.repoCache.repo)
93 if err != nil {
94 return err
95 }
96
97 return c.AddCommentRaw(author, time.Now().Unix(), message, files, nil)
98}
99
100func (c *BugCache) AddCommentRaw(author *identity.Identity, unixTime int64, message string, files []git.Hash, metadata map[string]string) error {
101 op, err := bug.AddCommentWithFiles(c.bug, author, unixTime, message, files)
102 if err != nil {
103 return err
104 }
105
106 for key, value := range metadata {
107 op.SetMetadata(key, value)
108 }
109
110 return c.notifyUpdated()
111}
112
113func (c *BugCache) ChangeLabels(added []string, removed []string) ([]bug.LabelChangeResult, error) {
114 author, err := identity.GetIdentity(c.repoCache.repo)
115 if err != nil {
116 return nil, err
117 }
118
119 return c.ChangeLabelsRaw(author, time.Now().Unix(), added, removed, nil)
120}
121
122func (c *BugCache) ChangeLabelsRaw(author *identity.Identity, unixTime int64, added []string, removed []string, metadata map[string]string) ([]bug.LabelChangeResult, error) {
123 changes, op, err := bug.ChangeLabels(c.bug, author, unixTime, added, removed)
124 if err != nil {
125 return changes, err
126 }
127
128 for key, value := range metadata {
129 op.SetMetadata(key, value)
130 }
131
132 err = c.notifyUpdated()
133 if err != nil {
134 return nil, err
135 }
136
137 return changes, nil
138}
139
140func (c *BugCache) Open() error {
141 author, err := identity.GetIdentity(c.repoCache.repo)
142 if err != nil {
143 return err
144 }
145
146 return c.OpenRaw(author, time.Now().Unix(), nil)
147}
148
149func (c *BugCache) OpenRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error {
150 op, err := bug.Open(c.bug, author, unixTime)
151 if err != nil {
152 return err
153 }
154
155 for key, value := range metadata {
156 op.SetMetadata(key, value)
157 }
158
159 return c.notifyUpdated()
160}
161
162func (c *BugCache) Close() error {
163 author, err := identity.GetIdentity(c.repoCache.repo)
164 if err != nil {
165 return err
166 }
167
168 return c.CloseRaw(author, time.Now().Unix(), nil)
169}
170
171func (c *BugCache) CloseRaw(author *identity.Identity, unixTime int64, metadata map[string]string) error {
172 op, err := bug.Close(c.bug, author, unixTime)
173 if err != nil {
174 return err
175 }
176
177 for key, value := range metadata {
178 op.SetMetadata(key, value)
179 }
180
181 return c.notifyUpdated()
182}
183
184func (c *BugCache) SetTitle(title string) error {
185 author, err := identity.GetIdentity(c.repoCache.repo)
186 if err != nil {
187 return err
188 }
189
190 return c.SetTitleRaw(author, time.Now().Unix(), title, nil)
191}
192
193func (c *BugCache) SetTitleRaw(author *identity.Identity, unixTime int64, title string, metadata map[string]string) error {
194 op, err := bug.SetTitle(c.bug, author, unixTime, title)
195 if err != nil {
196 return err
197 }
198
199 for key, value := range metadata {
200 op.SetMetadata(key, value)
201 }
202
203 return c.notifyUpdated()
204}
205
206func (c *BugCache) EditComment(target git.Hash, message string) error {
207 author, err := identity.GetIdentity(c.repoCache.repo)
208 if err != nil {
209 return err
210 }
211
212 return c.EditCommentRaw(author, time.Now().Unix(), target, message, nil)
213}
214
215func (c *BugCache) EditCommentRaw(author *identity.Identity, unixTime int64, target git.Hash, message string, metadata map[string]string) error {
216 op, err := bug.EditComment(c.bug, author, unixTime, target, message)
217 if err != nil {
218 return err
219 }
220
221 for key, value := range metadata {
222 op.SetMetadata(key, value)
223 }
224
225 return c.notifyUpdated()
226}
227
228func (c *BugCache) Commit() error {
229 return c.bug.Commit(c.repoCache.repo)
230}
231
232func (c *BugCache) CommitAsNeeded() error {
233 if c.bug.HasPendingOp() {
234 return c.bug.Commit(c.repoCache.repo)
235 }
236 return nil
237}