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	// Shortcut to resolve on the default repo for convenience
 20	DefaultResolveBug(id string) (BugCacher, error)
 21	DefaultResolveBugPrefix(prefix string) (BugCacher, error)
 22}
 23
 24type RepoCacher interface {
 25	ResolveBug(id string) (BugCacher, error)
 26	ResolveBugPrefix(prefix string) (BugCacher, error)
 27	AllBugIds() ([]string, error)
 28	ClearAllBugs()
 29
 30	// Mutations
 31
 32	NewBug(title string, message string) (BugCacher, error)
 33}
 34
 35type BugCacher interface {
 36	Snapshot() *bug.Snapshot
 37	ClearSnapshot()
 38}
 39
 40// Cacher ------------------------
 41
 42type RootCache struct {
 43	repos map[string]RepoCacher
 44}
 45
 46func NewCache() RootCache {
 47	return RootCache{
 48		repos: make(map[string]RepoCacher),
 49	}
 50}
 51
 52func (c *RootCache) RegisterRepository(ref string, repo repository.Repo) {
 53	c.repos[ref] = NewRepoCache(repo)
 54}
 55
 56func (c *RootCache) RegisterDefaultRepository(repo repository.Repo) {
 57	c.repos[""] = NewRepoCache(repo)
 58}
 59
 60func (c *RootCache) DefaultRepo() (RepoCacher, error) {
 61	if len(c.repos) != 1 {
 62		return nil, fmt.Errorf("repository is not unique")
 63	}
 64
 65	for _, r := range c.repos {
 66		return r, nil
 67	}
 68
 69	panic("unreachable")
 70}
 71
 72func (c *RootCache) ResolveRepo(ref string) (RepoCacher, error) {
 73	r, ok := c.repos[ref]
 74	if !ok {
 75		return nil, fmt.Errorf("unknown repo")
 76	}
 77	return r, nil
 78}
 79
 80func (c *RootCache) DefaultResolveBug(id string) (BugCacher, error) {
 81	repo, err := c.DefaultRepo()
 82
 83	if err != nil {
 84		return nil, err
 85	}
 86
 87	return repo.ResolveBug(id)
 88}
 89
 90func (c *RootCache) DefaultResolveBugPrefix(prefix string) (BugCacher, error) {
 91	repo, err := c.DefaultRepo()
 92
 93	if err != nil {
 94		return nil, err
 95	}
 96
 97	return repo.ResolveBugPrefix(prefix)
 98}
 99
100// Repo ------------------------
101
102type RepoCache struct {
103	repo repository.Repo
104	bugs map[string]BugCacher
105}
106
107func NewRepoCache(r repository.Repo) RepoCacher {
108	return &RepoCache{
109		repo: r,
110		bugs: make(map[string]BugCacher),
111	}
112}
113
114func (c RepoCache) ResolveBug(id string) (BugCacher, error) {
115	cached, ok := c.bugs[id]
116	if ok {
117		return cached, nil
118	}
119
120	b, err := bug.ReadLocalBug(c.repo, id)
121	if err != nil {
122		return nil, err
123	}
124
125	cached = NewBugCache(b)
126	c.bugs[id] = cached
127
128	return cached, nil
129}
130
131func (c RepoCache) ResolveBugPrefix(prefix string) (BugCacher, error) {
132	// preallocate but empty
133	matching := make([]string, 0, 5)
134
135	for id := range c.bugs {
136		if strings.HasPrefix(id, prefix) {
137			matching = append(matching, id)
138		}
139	}
140
141	// TODO: should check matching bug in the repo as well
142
143	if len(matching) > 1 {
144		return nil, fmt.Errorf("Multiple matching bug found:\n%s", strings.Join(matching, "\n"))
145	}
146
147	if len(matching) == 1 {
148		b := c.bugs[matching[0]]
149		return b, nil
150	}
151
152	b, err := bug.FindLocalBug(c.repo, prefix)
153
154	if err != nil {
155		return nil, err
156	}
157
158	cached := NewBugCache(b)
159	c.bugs[b.Id()] = cached
160
161	return cached, nil
162}
163
164func (c RepoCache) AllBugIds() ([]string, error) {
165	return bug.ListLocalIds(c.repo)
166}
167
168func (c RepoCache) ClearAllBugs() {
169	c.bugs = make(map[string]BugCacher)
170}
171
172func (c RepoCache) NewBug(title string, message string) (BugCacher, error) {
173	author, err := bug.GetUser(c.repo)
174	if err != nil {
175		return nil, err
176	}
177
178	b, err := operations.Create(author, title, message)
179	if err != nil {
180		return nil, err
181	}
182
183	err = b.Commit(c.repo)
184	if err != nil {
185		return nil, err
186	}
187
188	cached := NewBugCache(b)
189	c.bugs[b.Id()] = cached
190
191	return cached, nil
192}
193
194// Bug ------------------------
195
196type BugCache struct {
197	bug  *bug.Bug
198	snap *bug.Snapshot
199}
200
201func NewBugCache(b *bug.Bug) BugCacher {
202	return &BugCache{
203		bug: b,
204	}
205}
206
207func (c BugCache) Snapshot() *bug.Snapshot {
208	if c.snap == nil {
209		snap := c.bug.Compile()
210		c.snap = &snap
211	}
212	return c.snap
213}
214
215func (c BugCache) ClearSnapshot() {
216	c.snap = nil
217}