cache.go

  1package cache
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/MichaelMure/git-bug/bug"
  8	"github.com/MichaelMure/git-bug/bug/operations"
  9	"github.com/MichaelMure/git-bug/repository"
 10	"github.com/MichaelMure/git-bug/util"
 11)
 12
 13type Cacher interface {
 14	RegisterRepository(ref string, repo repository.Repo)
 15	RegisterDefaultRepository(repo repository.Repo)
 16
 17	ResolveRepo(ref string) (RepoCacher, error)
 18	DefaultRepo() (RepoCacher, error)
 19}
 20
 21type RepoCacher interface {
 22	Repository() repository.Repo
 23	ResolveBug(id string) (BugCacher, error)
 24	ResolveBugPrefix(prefix string) (BugCacher, error)
 25	AllBugIds() ([]string, error)
 26	ClearAllBugs()
 27
 28	// Mutations
 29	NewBug(title string, message string) (BugCacher, error)
 30	NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error)
 31}
 32
 33type BugCacher interface {
 34	Snapshot() *bug.Snapshot
 35	ClearSnapshot()
 36
 37	// Mutations
 38	AddComment(message string) error
 39	AddCommentWithFiles(message string, files []util.Hash) error
 40	ChangeLabels(added []string, removed []string) error
 41	Open() error
 42	Close() error
 43	SetTitle(title string) error
 44
 45	Commit() error
 46}
 47
 48// Cacher ------------------------
 49
 50type RootCache struct {
 51	repos map[string]RepoCacher
 52}
 53
 54func NewCache() RootCache {
 55	return RootCache{
 56		repos: make(map[string]RepoCacher),
 57	}
 58}
 59
 60func (c *RootCache) RegisterRepository(ref string, repo repository.Repo) {
 61	c.repos[ref] = NewRepoCache(repo)
 62}
 63
 64func (c *RootCache) RegisterDefaultRepository(repo repository.Repo) {
 65	c.repos[""] = NewRepoCache(repo)
 66}
 67
 68func (c *RootCache) DefaultRepo() (RepoCacher, error) {
 69	if len(c.repos) != 1 {
 70		return nil, fmt.Errorf("repository is not unique")
 71	}
 72
 73	for _, r := range c.repos {
 74		return r, nil
 75	}
 76
 77	panic("unreachable")
 78}
 79
 80func (c *RootCache) ResolveRepo(ref string) (RepoCacher, error) {
 81	r, ok := c.repos[ref]
 82	if !ok {
 83		return nil, fmt.Errorf("unknown repo")
 84	}
 85	return r, nil
 86}
 87
 88// Repo ------------------------
 89
 90type RepoCache struct {
 91	repo repository.Repo
 92	bugs map[string]BugCacher
 93}
 94
 95func NewRepoCache(r repository.Repo) RepoCacher {
 96	return &RepoCache{
 97		repo: r,
 98		bugs: make(map[string]BugCacher),
 99	}
100}
101
102func (c *RepoCache) Repository() repository.Repo {
103	return c.repo
104}
105
106func (c *RepoCache) ResolveBug(id string) (BugCacher, error) {
107	cached, ok := c.bugs[id]
108	if ok {
109		return cached, nil
110	}
111
112	b, err := bug.ReadLocalBug(c.repo, id)
113	if err != nil {
114		return nil, err
115	}
116
117	cached = NewBugCache(c.repo, b)
118	c.bugs[id] = cached
119
120	return cached, nil
121}
122
123func (c *RepoCache) ResolveBugPrefix(prefix string) (BugCacher, error) {
124	// preallocate but empty
125	matching := make([]string, 0, 5)
126
127	for id := range c.bugs {
128		if strings.HasPrefix(id, prefix) {
129			matching = append(matching, id)
130		}
131	}
132
133	// TODO: should check matching bug in the repo as well
134
135	if len(matching) > 1 {
136		return nil, fmt.Errorf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
137	}
138
139	if len(matching) == 1 {
140		b := c.bugs[matching[0]]
141		return b, nil
142	}
143
144	b, err := bug.FindLocalBug(c.repo, prefix)
145
146	if err != nil {
147		return nil, err
148	}
149
150	cached := NewBugCache(c.repo, b)
151	c.bugs[b.Id()] = cached
152
153	return cached, nil
154}
155
156func (c *RepoCache) AllBugIds() ([]string, error) {
157	return bug.ListLocalIds(c.repo)
158}
159
160func (c *RepoCache) ClearAllBugs() {
161	c.bugs = make(map[string]BugCacher)
162}
163
164func (c *RepoCache) NewBug(title string, message string) (BugCacher, error) {
165	return c.NewBugWithFiles(title, message, nil)
166}
167
168func (c *RepoCache) NewBugWithFiles(title string, message string, files []util.Hash) (BugCacher, error) {
169	author, err := bug.GetUser(c.repo)
170	if err != nil {
171		return nil, err
172	}
173
174	b, err := operations.CreateWithFiles(author, title, message, files)
175	if err != nil {
176		return nil, err
177	}
178
179	err = b.Commit(c.repo)
180	if err != nil {
181		return nil, err
182	}
183
184	cached := NewBugCache(c.repo, b)
185	c.bugs[b.Id()] = cached
186
187	return cached, nil
188}
189
190// Bug ------------------------
191
192type BugCache struct {
193	repo repository.Repo
194	bug  *bug.Bug
195	snap *bug.Snapshot
196}
197
198func NewBugCache(repo repository.Repo, b *bug.Bug) BugCacher {
199	return &BugCache{
200		repo: repo,
201		bug:  b,
202	}
203}
204
205func (c *BugCache) Snapshot() *bug.Snapshot {
206	if c.snap == nil {
207		snap := c.bug.Compile()
208		c.snap = &snap
209	}
210	return c.snap
211}
212
213func (c *BugCache) ClearSnapshot() {
214	c.snap = nil
215}
216
217func (c *BugCache) AddComment(message string) error {
218	return c.AddCommentWithFiles(message, nil)
219}
220
221func (c *BugCache) AddCommentWithFiles(message string, files []util.Hash) error {
222	author, err := bug.GetUser(c.repo)
223	if err != nil {
224		return err
225	}
226
227	operations.CommentWithFiles(c.bug, author, message, files)
228
229	// TODO: perf --> the snapshot could simply be updated with the new op
230	c.ClearSnapshot()
231
232	return nil
233}
234
235func (c *BugCache) ChangeLabels(added []string, removed []string) error {
236	author, err := bug.GetUser(c.repo)
237	if err != nil {
238		return err
239	}
240
241	err = operations.ChangeLabels(nil, c.bug, author, added, removed)
242	if err != nil {
243		return err
244	}
245
246	// TODO: perf --> the snapshot could simply be updated with the new op
247	c.ClearSnapshot()
248
249	return nil
250}
251
252func (c *BugCache) Open() error {
253	author, err := bug.GetUser(c.repo)
254	if err != nil {
255		return err
256	}
257
258	operations.Open(c.bug, author)
259
260	// TODO: perf --> the snapshot could simply be updated with the new op
261	c.ClearSnapshot()
262
263	return nil
264}
265
266func (c *BugCache) Close() error {
267	author, err := bug.GetUser(c.repo)
268	if err != nil {
269		return err
270	}
271
272	operations.Close(c.bug, author)
273
274	// TODO: perf --> the snapshot could simply be updated with the new op
275	c.ClearSnapshot()
276
277	return nil
278}
279
280func (c *BugCache) SetTitle(title string) error {
281	author, err := bug.GetUser(c.repo)
282	if err != nil {
283		return err
284	}
285
286	operations.SetTitle(c.bug, author, title)
287
288	// TODO: perf --> the snapshot could simply be updated with the new op
289	c.ClearSnapshot()
290
291	return nil
292}
293
294func (c *BugCache) Commit() error {
295	return c.bug.Commit(c.repo)
296}