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