cache.go

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