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