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