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