1package cache
2
3import (
4 "fmt"
5
6 "github.com/pkg/errors"
7
8 "github.com/MichaelMure/git-bug/bug"
9 "github.com/MichaelMure/git-bug/entity"
10 "github.com/MichaelMure/git-bug/identity"
11 "github.com/MichaelMure/git-bug/repository"
12)
13
14func (c *RepoCache) Name() string {
15 return c.name
16}
17
18// LocalConfig give access to the repository scoped configuration
19func (c *RepoCache) LocalConfig() repository.Config {
20 return c.repo.LocalConfig()
21}
22
23func (c *RepoCache) Keyring() repository.Keyring {
24 return c.repo.Keyring()
25}
26
27// GetPath returns the path to the repo.
28func (c *RepoCache) GetPath() string {
29 return c.repo.GetPath()
30}
31
32// GetCoreEditor returns the name of the editor that the user has used to configure git.
33func (c *RepoCache) GetCoreEditor() (string, error) {
34 return c.repo.GetCoreEditor()
35}
36
37// GetRemotes returns the configured remotes repositories.
38func (c *RepoCache) GetRemotes() (map[string]string, error) {
39 return c.repo.GetRemotes()
40}
41
42// GetUserName returns the name the the user has used to configure git
43func (c *RepoCache) GetUserName() (string, error) {
44 return c.repo.GetUserName()
45}
46
47// GetUserEmail returns the email address that the user has used to configure git.
48func (c *RepoCache) GetUserEmail() (string, error) {
49 return c.repo.GetUserEmail()
50}
51
52// ReadData will attempt to read arbitrary data from the given hash
53func (c *RepoCache) ReadData(hash repository.Hash) ([]byte, error) {
54 return c.repo.ReadData(hash)
55}
56
57// StoreData will store arbitrary data and return the corresponding hash
58func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
59 return c.repo.StoreData(data)
60}
61
62// Fetch retrieve updates from a remote
63// This does not change the local bugs or identities state
64func (c *RepoCache) Fetch(remote string) (string, error) {
65 stdout1, err := identity.Fetch(c.repo, remote)
66 if err != nil {
67 return stdout1, err
68 }
69
70 stdout2, err := bug.Fetch(c.repo, remote)
71 if err != nil {
72 return stdout2, err
73 }
74
75 return stdout1 + stdout2, nil
76}
77
78// MergeAll will merge all the available remote bug and identities
79func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
80 out := make(chan entity.MergeResult)
81
82 // Intercept merge results to update the cache properly
83 go func() {
84 defer close(out)
85
86 results := identity.MergeAll(c.repo, remote)
87 for result := range results {
88 out <- result
89
90 if result.Err != nil {
91 continue
92 }
93
94 switch result.Status {
95 case entity.MergeStatusNew, entity.MergeStatusUpdated:
96 i := result.Entity.(*identity.Identity)
97 c.muIdentity.Lock()
98 c.identitiesExcerpts[result.Id] = NewIdentityExcerpt(i)
99 c.muIdentity.Unlock()
100 }
101 }
102
103 results = bug.MergeAll(c.repo, remote)
104 for result := range results {
105 out <- result
106
107 if result.Err != nil {
108 continue
109 }
110
111 switch result.Status {
112 case entity.MergeStatusNew, entity.MergeStatusUpdated:
113 b := result.Entity.(*bug.Bug)
114 snap := b.Compile()
115 c.muBug.Lock()
116 c.bugExcerpts[result.Id] = NewBugExcerpt(b, &snap)
117 c.muBug.Unlock()
118 }
119 }
120
121 err := c.write()
122
123 // No easy way out here ..
124 if err != nil {
125 panic(err)
126 }
127 }()
128
129 return out
130}
131
132// Push update a remote with the local changes
133func (c *RepoCache) Push(remote string) (string, error) {
134 stdout1, err := identity.Push(c.repo, remote)
135 if err != nil {
136 return stdout1, err
137 }
138
139 stdout2, err := bug.Push(c.repo, remote)
140 if err != nil {
141 return stdout2, err
142 }
143
144 return stdout1 + stdout2, nil
145}
146
147// Pull will do a Fetch + MergeAll
148// This function will return an error if a merge fail
149func (c *RepoCache) Pull(remote string) error {
150 _, err := c.Fetch(remote)
151 if err != nil {
152 return err
153 }
154
155 for merge := range c.MergeAll(remote) {
156 if merge.Err != nil {
157 return merge.Err
158 }
159 if merge.Status == entity.MergeStatusInvalid {
160 return errors.Errorf("merge failure: %s", merge.Reason)
161 }
162 }
163
164 return nil
165}
166
167func (c *RepoCache) SetUserIdentity(i *IdentityCache) error {
168 err := identity.SetUserIdentity(c.repo, i.Identity)
169 if err != nil {
170 return err
171 }
172
173 c.muIdentity.RLock()
174 defer c.muIdentity.RUnlock()
175
176 // Make sure that everything is fine
177 if _, ok := c.identities[i.Id()]; !ok {
178 panic("SetUserIdentity while the identity is not from the cache, something is wrong")
179 }
180
181 c.userIdentityId = i.Id()
182
183 return nil
184}
185
186func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) {
187 if c.userIdentityId != "" {
188 i, ok := c.identities[c.userIdentityId]
189 if ok {
190 return i, nil
191 }
192 }
193
194 c.muIdentity.Lock()
195 defer c.muIdentity.Unlock()
196
197 i, err := identity.GetUserIdentity(c.repo)
198 if err != nil {
199 return nil, err
200 }
201
202 cached := NewIdentityCache(c, i)
203 c.identities[i.Id()] = cached
204 c.userIdentityId = i.Id()
205
206 return cached, nil
207}
208
209func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
210 if c.userIdentityId == "" {
211 id, err := identity.GetUserIdentityId(c.repo)
212 if err != nil {
213 return nil, err
214 }
215 c.userIdentityId = id
216 }
217
218 c.muIdentity.RLock()
219 defer c.muIdentity.RUnlock()
220
221 excerpt, ok := c.identitiesExcerpts[c.userIdentityId]
222 if !ok {
223 return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId)
224 }
225 return excerpt, nil
226}
227
228func (c *RepoCache) IsUserIdentitySet() (bool, error) {
229 return identity.IsUserIdentitySet(c.repo)
230}