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}