diff options
-rw-r--r-- | cache/bug_cache.go | 6 | ||||
-rw-r--r-- | cache/bug_subcache.go | 96 | ||||
-rw-r--r-- | cache/cached.go | 5 | ||||
-rw-r--r-- | cache/identity_cache.go | 10 | ||||
-rw-r--r-- | cache/identity_subcache.go | 33 | ||||
-rw-r--r-- | cache/repo_cache.go | 86 | ||||
-rw-r--r-- | cache/repo_cache_common.go | 64 | ||||
-rw-r--r-- | cache/subcache.go | 80 | ||||
-rw-r--r-- | commands/execenv/env.go | 21 | ||||
-rw-r--r-- | entity/resolver.go | 8 |
10 files changed, 204 insertions, 205 deletions
diff --git a/cache/bug_cache.go b/cache/bug_cache.go index 0fd52ff1..7b3fa114 100644 --- a/cache/bug_cache.go +++ b/cache/bug_cache.go @@ -22,12 +22,12 @@ type BugCache struct { CachedEntityBase[*bug.Snapshot, bug.Operation] } -func NewBugCache(subcache *RepoCacheBug, getUserIdentity func() (identity.Interface, error), b *bug.Bug) *BugCache { +func NewBugCache(b *bug.Bug, repo repository.ClockedRepo, getUserIdentity getUserIdentityFunc, entityUpdated func(id entity.Id) error) *BugCache { return &BugCache{ CachedEntityBase: CachedEntityBase[*bug.Snapshot, bug.Operation]{ - entityUpdated: subcache.entityUpdated, + repo: repo, + entityUpdated: entityUpdated, getUserIdentity: getUserIdentity, - repo: subcache.repo, entity: &bug.WithSnapshot{Bug: b}, }, } diff --git a/cache/bug_subcache.go b/cache/bug_subcache.go index c8901754..f0a8889b 100644 --- a/cache/bug_subcache.go +++ b/cache/bug_subcache.go @@ -2,13 +2,8 @@ package cache import ( "errors" - "fmt" "sort" - "strings" "time" - "unicode/utf8" - - "github.com/blevesearch/bleve" "github.com/MichaelMure/git-bug/entities/bug" "github.com/MichaelMure/git-bug/entities/identity" @@ -18,7 +13,39 @@ import ( ) type RepoCacheBug struct { - *SubCache[*BugExcerpt, *BugCache, bug.Interface] + *SubCache[*bug.Bug, *BugExcerpt, *BugCache] +} + +func NewRepoCacheBug(repo repository.ClockedRepo, + resolvers func() entity.Resolvers, + getUserIdentity getUserIdentityFunc) *RepoCacheBug { + + makeCached := func(b *bug.Bug, entityUpdated func(id entity.Id) error) *BugCache { + return NewBugCache(b, repo, getUserIdentity, entityUpdated) + } + + makeExcerpt := func(b *bug.Bug) *BugExcerpt { + return NewBugExcerpt(b, b.Compile()) + } + + makeIndex := func(b *BugCache) []string { + snap := b.Snapshot() + var res []string + for _, comment := range snap.Comments { + res = append(res, comment.Message) + } + res = append(res, snap.Title) + return res + } + + sc := NewSubCache[*bug.Bug, *BugExcerpt, *BugCache]( + repo, resolvers, getUserIdentity, + makeCached, makeExcerpt, makeIndex, + "bug", "bugs", + formatVersion, defaultMaxLoadedBugs, + ) + + return &RepoCacheBug{SubCache: sc} } // ResolveBugCreateMetadata retrieve a bug that has the exact given metadata on @@ -94,29 +121,19 @@ func (c *RepoCacheBug) QueryBugs(q *query.Query) ([]entity.Id, error) { if q.Search != nil { foundBySearch = map[entity.Id]*BugExcerpt{} - terms := make([]string, len(q.Search)) - copy(terms, q.Search) - for i, search := range q.Search { - if strings.Contains(search, " ") { - terms[i] = fmt.Sprintf("\"%s\"", search) - } - } - - bleveQuery := bleve.NewQueryStringQuery(strings.Join(terms, " ")) - bleveSearch := bleve.NewSearchRequest(bleveQuery) - - index, err := c.repo.GetBleveIndex("bug") + index, err := c.repo.GetIndex("bug") if err != nil { return nil, err } - searchResults, err := index.Search(bleveSearch) + res, err := index.Search(q.Search) if err != nil { return nil, err } - for _, hit := range searchResults.Hits { - foundBySearch[entity.Id(hit.ID)] = c.excerpts[entity.Id(hit.ID)] + for _, hit := range res { + id := entity.Id(hit) + foundBySearch[id] = c.excerpts[id] } } else { foundBySearch = c.excerpts @@ -232,40 +249,3 @@ func (c *RepoCacheBug) NewRaw(author identity.Interface, unixTime int64, title s return cached, op, nil } - -func (c *RepoCacheBug) addBugToSearchIndex(snap *bug.Snapshot) error { - searchableBug := struct { - Text []string - }{} - - // See https://github.com/blevesearch/bleve/issues/1576 - var sb strings.Builder - normalize := func(text string) string { - sb.Reset() - for _, field := range strings.Fields(text) { - if utf8.RuneCountInString(field) < 100 { - sb.WriteString(field) - sb.WriteRune(' ') - } - } - return sb.String() - } - - for _, comment := range snap.Comments { - searchableBug.Text = append(searchableBug.Text, normalize(comment.Message)) - } - - searchableBug.Text = append(searchableBug.Text, normalize(snap.Title)) - - index, err := c.repo.GetBleveIndex("bug") - if err != nil { - return err - } - - err = index.Index(snap.Id().String(), searchableBug) - if err != nil { - return err - } - - return nil -} diff --git a/cache/cached.go b/cache/cached.go index 75ca58e5..74757356 100644 --- a/cache/cached.go +++ b/cache/cached.go @@ -4,7 +4,6 @@ import ( "sync" "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/entity/dag" "github.com/MichaelMure/git-bug/repository" @@ -57,9 +56,9 @@ import ( // } type CachedEntityBase[SnapT dag.Snapshot, OpT dag.Operation] struct { - entityUpdated func(id entity.Id) error - getUserIdentity func() (identity.Interface, error) repo repository.ClockedRepo + entityUpdated func(id entity.Id) error + getUserIdentity getUserIdentityFunc mu sync.RWMutex entity dag.Interface[SnapT, OpT] diff --git a/cache/identity_cache.go b/cache/identity_cache.go index a6c929ad..00f5ae95 100644 --- a/cache/identity_cache.go +++ b/cache/identity_cache.go @@ -10,17 +10,17 @@ var _ identity.Interface = &IdentityCache{} // IdentityCache is a wrapper around an Identity for caching. type IdentityCache struct { - entityUpdated func(id entity.Id) error repo repository.ClockedRepo + entityUpdated func(id entity.Id) error *identity.Identity } -func NewIdentityCache(subcache *RepoCacheIdentity, id *identity.Identity) *IdentityCache { +func NewIdentityCache(i *identity.Identity, repo repository.ClockedRepo, entityUpdated func(id entity.Id) error) *IdentityCache { return &IdentityCache{ - entityUpdated: subcache.entityUpdated, - repo: subcache.repo, - Identity: id, + repo: repo, + entityUpdated: entityUpdated, + Identity: i, } } diff --git a/cache/identity_subcache.go b/cache/identity_subcache.go index a3b5a0f8..b175d731 100644 --- a/cache/identity_subcache.go +++ b/cache/identity_subcache.go @@ -4,10 +4,39 @@ import ( "fmt" "github.com/MichaelMure/git-bug/entities/identity" + "github.com/MichaelMure/git-bug/entity" + "github.com/MichaelMure/git-bug/repository" ) type RepoCacheIdentity struct { - SubCache[*IdentityExcerpt, *IdentityCache, identity.Interface] + *SubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache] +} + +func NewRepoCacheIdentity(repo repository.ClockedRepo, + resolvers func() entity.Resolvers, + getUserIdentity getUserIdentityFunc) *RepoCacheIdentity { + + makeCached := func(i *identity.Identity, entityUpdated func(id entity.Id) error) *IdentityCache { + return NewIdentityCache(i, repo, entityUpdated) + } + + makeExcerpt := func(i *identity.Identity) *IdentityExcerpt { + return NewIdentityExcerpt(i) + } + + makeIndex := func(i *IdentityCache) []string { + // no indexing + return nil + } + + sc := NewSubCache[*identity.Identity, *IdentityExcerpt, *IdentityCache]( + repo, resolvers, getUserIdentity, + makeCached, makeExcerpt, makeIndex, + "identity", "identities", + formatVersion, defaultMaxLoadedBugs, + ) + + return &RepoCacheIdentity{SubCache: sc} } // ResolveIdentityImmutableMetadata retrieve an Identity that has the exact given metadata on @@ -65,7 +94,7 @@ func (c *RepoCacheIdentity) finishIdentity(i *identity.Identity, metadata map[st return nil, fmt.Errorf("identity %s already exist in the cache", i.Id()) } - cached := NewIdentityCache(c, i) + cached := NewIdentityCache(i, c.repo, c.entityUpdated) c.cached[i.Id()] = cached c.mu.Unlock() diff --git a/cache/repo_cache.go b/cache/repo_cache.go index 982804b5..b2facac3 100644 --- a/cache/repo_cache.go +++ b/cache/repo_cache.go @@ -8,8 +8,6 @@ import ( "strconv" "sync" - "github.com/MichaelMure/git-bug/entities/bug" - "github.com/MichaelMure/git-bug/entities/identity" "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/repository" "github.com/MichaelMure/git-bug/util/multierr" @@ -67,6 +65,7 @@ type RepoCache struct { subcaches []cacheMgmt // the user identity's id, if known + muUserIdentity sync.RWMutex userIdentityId entity.Id } @@ -80,17 +79,15 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, chan name: name, } - bugs := NewSubCache[*BugExcerpt, *BugCache, bug.Interface](r, - c.getResolvers, c.GetUserIdentity, - "bug", "bugs", - formatVersion, defaultMaxLoadedBugs) + c.identities = NewRepoCacheIdentity(r, c.getResolvers, c.GetUserIdentity) + c.subcaches = append(c.subcaches, c.identities) - c.subcaches = append(c.subcaches, bugs) - c.bugs = &RepoCacheBug{SubCache: *bugs} + c.bugs = NewRepoCacheBug(r, c.getResolvers, c.GetUserIdentity) + c.subcaches = append(c.subcaches, c.bugs) c.resolvers = entity.Resolvers{ - &IdentityCache{}: entity.ResolverFunc((func(id entity.Id) (entity.Interface, error)(c.identities.Resolve)), - &BugCache{}: c.bugs, + &IdentityCache{}: entity.ResolverFunc[*IdentityCache](c.identities.Resolve), + &BugCache{}: entity.ResolverFunc[*BugCache](c.bugs.Resolve), } err := c.lock() @@ -104,12 +101,9 @@ func NewNamedRepoCache(r repository.ClockedRepo, name string) (*RepoCache, chan } // Cache is either missing, broken or outdated. Rebuilding. - err = c.buildCache() - if err != nil { - return nil, err - } + events := c.buildCache() - return c, c.write() + return c, events, nil } // Bugs gives access to the Bug entities @@ -198,8 +192,8 @@ const ( type BuildEvent struct { Typename string - Event BuildEventType - Err error + Event BuildEventType + Err error } func (c *RepoCache) buildCache() chan BuildEvent { @@ -222,14 +216,23 @@ func (c *RepoCache) buildCache() chan BuildEvent { if err != nil { out <- BuildEvent{ Typename: subcache.Typename(), - Err: err, + Err: err, + } + return + } + + err = subcache.Write() + if err != nil { + out <- BuildEvent{ + Typename: subcache.Typename(), + Err: err, } return } out <- BuildEvent{ Typename: subcache.Typename(), - Event: BuildEventFinished, + Event: BuildEventFinished, } }(subcache) } @@ -237,51 +240,6 @@ func (c *RepoCache) buildCache() chan BuildEvent { }() return out - - _, _ = fmt.Fprintf(os.Stderr, "Building identity cache... ") - - c.identitiesExcerpts = make(map[entity.Id]*IdentityExcerpt) - - allIdentities := identity.ReadAllLocal(c.repo) - - for i := range allIdentities { - if i.Err != nil { - return i.Err - } - - c.identitiesExcerpts[i.Identity.Id()] = NewIdentityExcerpt(i.Identity) - } - - _, _ = fmt.Fprintln(os.Stderr, "Done.") - - _, _ = fmt.Fprintf(os.Stderr, "Building bug cache... ") - - c.bugExcerpts = make(map[entity.Id]*BugExcerpt) - - allBugs := bug.ReadAllWithResolver(c.repo, c.resolvers) - - // wipe the index just to be sure - err := c.repo.ClearBleveIndex("bug") - if err != nil { - return err - } - - for b := range allBugs { - if b.Err != nil { - return b.Err - } - - snap := b.Bug.Compile() - c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, snap) - - if err := c.addBugToSearchIndex(snap); err != nil { - return err - } - } - - _, _ = fmt.Fprintln(os.Stderr, "Done.") - - return nil } // repoIsAvailable check is the given repository is locked by a Cache. diff --git a/cache/repo_cache_common.go b/cache/repo_cache_common.go index 18ba52f3..1aed04b2 100644 --- a/cache/repo_cache_common.go +++ b/cache/repo_cache_common.go @@ -1,8 +1,6 @@ package cache import ( - "fmt" - "github.com/go-git/go-billy/v5" "github.com/pkg/errors" @@ -186,64 +184,64 @@ func (c *RepoCache) Pull(remote string) error { } func (c *RepoCache) SetUserIdentity(i *IdentityCache) error { - err := identity.SetUserIdentity(c.repo, i.Identity) - if err != nil { - return err - } - - c.muIdentity.RLock() - defer c.muIdentity.RUnlock() + c.muUserIdentity.RLock() + defer c.muUserIdentity.RUnlock() // Make sure that everything is fine - if _, ok := c.identities[i.Id()]; !ok { + if _, err := c.identities.Resolve(i.Id()); err != nil { panic("SetUserIdentity while the identity is not from the cache, something is wrong") } + err := identity.SetUserIdentity(c.repo, i.Identity) + if err != nil { + return err + } + c.userIdentityId = i.Id() return nil } func (c *RepoCache) GetUserIdentity() (*IdentityCache, error) { + c.muUserIdentity.RLock() if c.userIdentityId != "" { - i, ok := c.identities[c.userIdentityId] - if ok { - return i, nil - } + defer c.muUserIdentity.RUnlock() + return c.identities.Resolve(c.userIdentityId) } + c.muUserIdentity.RUnlock() - c.muIdentity.Lock() - defer c.muIdentity.Unlock() + c.muUserIdentity.Lock() + defer c.muUserIdentity.Unlock() - i, err := identity.GetUserIdentity(c.repo) + i, err := identity.GetUserIdentityId(c.repo) if err != nil { return nil, err } - cached := NewIdentityCache(c, i) - c.identities[i.Id()] = cached - c.userIdentityId = i.Id() + c.userIdentityId = i - return cached, nil + return c.identities.Resolve(i) } func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) { - if c.userIdentityId == "" { - id, err := identity.GetUserIdentityId(c.repo) - if err != nil { - return nil, err - } - c.userIdentityId = id + c.muUserIdentity.RLock() + if c.userIdentityId != "" { + defer c.muUserIdentity.RUnlock() + return c.identities.ResolveExcerpt(c.userIdentityId) } + c.muUserIdentity.RUnlock() - c.muIdentity.RLock() - defer c.muIdentity.RUnlock() + c.muUserIdentity.Lock() + defer c.muUserIdentity.Unlock() - excerpt, ok := c.identitiesExcerpts[c.userIdentityId] - if !ok { - return nil, fmt.Errorf("cache: missing identity excerpt %v", c.userIdentityId) + i, err := identity.GetUserIdentityId(c.repo) + if err != nil { + return nil, err } - return excerpt, nil + + c.userIdentityId = i + + return c.identities.ResolveExcerpt(i) } func (c *RepoCache) IsUserIdentitySet() (bool, error) { diff --git a/cache/subcache.go b/cache/subcache.go index 285d4fe7..1737da43 100644 --- a/cache/subcache.go +++ b/cache/subcache.go @@ -19,20 +19,21 @@ type Excerpt interface { } type CacheEntity interface { + Id() entity.Id NeedCommit() bool } type getUserIdentityFunc func() (*IdentityCache, error) -type SubCache[ExcerptT Excerpt, CacheT CacheEntity, EntityT entity.Interface] struct { +type SubCache[EntityT entity.Interface, ExcerptT Excerpt, CacheT CacheEntity] struct { repo repository.ClockedRepo resolvers func() entity.Resolvers getUserIdentity getUserIdentityFunc readWithResolver func(repository.ClockedRepo, entity.Resolvers, entity.Id) (EntityT, error) - makeCached func(*SubCache[ExcerptT, CacheT, EntityT], getUserIdentityFunc, EntityT) CacheT - makeExcerpt func() Excerpt - indexingCallback func(CacheT) error + makeCached func(entity EntityT, entityUpdated func(id entity.Id) error) CacheT + makeExcerpt func(EntityT) ExcerptT + makeIndex func(CacheT) []string typename string namespace string @@ -45,13 +46,15 @@ type SubCache[ExcerptT Excerpt, CacheT CacheEntity, EntityT entity.Interface] st lru *lruIdCache } -func NewSubCache[ExcerptT Excerpt, CacheT CacheEntity, EntityT entity.Interface]( +func NewSubCache[EntityT entity.Interface, ExcerptT Excerpt, CacheT CacheEntity]( repo repository.ClockedRepo, - resolvers func() entity.Resolvers, - getUserIdentity getUserIdentityFunc, + resolvers func() entity.Resolvers, getUserIdentity getUserIdentityFunc, + makeCached func(entity EntityT, entityUpdated func(id entity.Id) error) CacheT, + makeExcerpt func(EntityT) ExcerptT, + makeIndex func(CacheT) []string, typename, namespace string, - version uint, maxLoaded int) *SubCache[ExcerptT, CacheT, EntityT] { - return &SubCache[ExcerptT, CacheT, EntityT]{ + version uint, maxLoaded int) *SubCache[EntityT, ExcerptT, CacheT] { + return &SubCache[EntityT, ExcerptT, CacheT]{ repo: repo, resolvers: resolvers, getUserIdentity: getUserIdentity, @@ -65,12 +68,12 @@ func NewSubCache[ExcerptT Excerpt, CacheT CacheEntity, EntityT entity.Interface] } } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Typename() string { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Typename() string { return sc.typename } // Load will try to read from the disk the entity cache file -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Load() error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Load() error { sc.mu.Lock() defer sc.mu.Unlock() @@ -97,7 +100,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Load() error { sc.excerpts = aux.Excerpts - index, err := sc.repo.GetBleveIndex("bug") + index, err := sc.repo.GetIndex(sc.typename) if err != nil { return err } @@ -115,7 +118,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Load() error { } // Write will serialize on disk the entity cache file -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Write() error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Write() error { sc.mu.RLock() defer sc.mu.RUnlock() @@ -149,19 +152,26 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Write() error { return f.Close() } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Build() error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Build() error { sc.excerpts = make(map[entity.Id]ExcerptT) sc.readWithResolver allBugs := bug.ReadAllWithResolver(c.repo, c.resolvers) + index, err := sc.repo.GetIndex(sc.typename) + if err != nil { + return err + } + // wipe the index just to be sure - err := c.repo.ClearBleveIndex("bug") + err = index.Clear() if err != nil { return err } + indexer, indexEnd := index.IndexBatch() + for b := range allBugs { if b.Err != nil { return b.Err @@ -170,15 +180,21 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Build() error { snap := b.Bug.Compile() c.bugExcerpts[b.Bug.Id()] = NewBugExcerpt(b.Bug, snap) - if err := c.addBugToSearchIndex(snap); err != nil { + if err := indexer(snap); err != nil { return err } } + err = indexEnd() + if err != nil { + return err + } + _, _ = fmt.Fprintln(os.Stderr, "Done.") + return nil } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Close() error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Close() error { sc.mu.Lock() defer sc.mu.Unlock() sc.excerpts = nil @@ -187,7 +203,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Close() error { } // AllIds return all known bug ids -func (sc *SubCache[ExcerptT, CacheT, EntityT]) AllIds() []entity.Id { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) AllIds() []entity.Id { sc.mu.RLock() defer sc.mu.RUnlock() @@ -203,7 +219,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) AllIds() []entity.Id { } // Resolve retrieve an entity matching the exact given id -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Resolve(id entity.Id) (CacheT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Resolve(id entity.Id) (CacheT, error) { sc.mu.RLock() cached, ok := sc.cached[id] if ok { @@ -213,12 +229,12 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Resolve(id entity.Id) (CacheT, er } sc.mu.RUnlock() - b, err := sc.readWithResolver(sc.repo, sc.resolvers(), id) + e, err := sc.readWithResolver(sc.repo, sc.resolvers(), id) if err != nil { return *new(CacheT), err } - cached = sc.makeCached(sc, sc.getUserIdentity, b) + cached = sc.makeCached(e, sc.entityUpdated) sc.mu.Lock() sc.cached[id] = cached @@ -232,13 +248,13 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Resolve(id entity.Id) (CacheT, er // ResolvePrefix retrieve an entity matching an id prefix. It fails if multiple // entity match. -func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolvePrefix(prefix string) (CacheT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) ResolvePrefix(prefix string) (CacheT, error) { return sc.ResolveMatcher(func(excerpt ExcerptT) bool { return excerpt.Id().HasPrefix(prefix) }) } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveMatcher(f func(ExcerptT) bool) (CacheT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) ResolveMatcher(f func(ExcerptT) bool) (CacheT, error) { id, err := sc.resolveMatcher(f) if err != nil { return *new(CacheT), err @@ -247,7 +263,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveMatcher(f func(ExcerptT) b } // ResolveExcerpt retrieve an Excerpt matching the exact given id -func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveExcerpt(id entity.Id) (ExcerptT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) ResolveExcerpt(id entity.Id) (ExcerptT, error) { sc.mu.RLock() defer sc.mu.RUnlock() @@ -261,13 +277,13 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveExcerpt(id entity.Id) (Exc // ResolveExcerptPrefix retrieve an Excerpt matching an id prefix. It fails if multiple // entity match. -func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveExcerptPrefix(prefix string) (ExcerptT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) ResolveExcerptPrefix(prefix string) (ExcerptT, error) { return sc.ResolveExcerptMatcher(func(excerpt ExcerptT) bool { return excerpt.Id().HasPrefix(prefix) }) } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveExcerptMatcher(f func(ExcerptT) bool) (ExcerptT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) ResolveExcerptMatcher(f func(ExcerptT) bool) (ExcerptT, error) { id, err := sc.resolveMatcher(f) if err != nil { return *new(ExcerptT), err @@ -275,7 +291,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) ResolveExcerptMatcher(f func(Exce return sc.ResolveExcerpt(id) } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) resolveMatcher(f func(ExcerptT) bool) (entity.Id, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) resolveMatcher(f func(ExcerptT) bool) (entity.Id, error) { sc.mu.RLock() defer sc.mu.RUnlock() @@ -301,14 +317,14 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) resolveMatcher(f func(ExcerptT) b var errNotInCache = errors.New("entity missing from cache") -func (sc *SubCache[ExcerptT, CacheT, EntityT]) add(e EntityT) (CacheT, error) { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) add(e EntityT) (CacheT, error) { sc.mu.Lock() if _, has := sc.cached[e.Id()]; has { sc.mu.Unlock() return *new(CacheT), fmt.Errorf("entity %s already exist in the cache", e.Id()) } - cached := sc.makeCached(sc, sc.getUserIdentity, e) + cached := sc.makeCached(e, sc.entityUpdated) sc.cached[e.Id()] = cached sc.lru.Add(e.Id()) sc.mu.Unlock() @@ -324,7 +340,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) add(e EntityT) (CacheT, error) { return cached, nil } -func (sc *SubCache[ExcerptT, CacheT, EntityT]) Remove(prefix string) error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) Remove(prefix string) error { e, err := sc.ResolvePrefix(prefix) if err != nil { return err @@ -349,7 +365,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) Remove(prefix string) error { } // entityUpdated is a callback to trigger when the excerpt of an entity changed -func (sc *SubCache[ExcerptT, CacheT, EntityT]) entityUpdated(id entity.Id) error { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) entityUpdated(id entity.Id) error { sc.mu.Lock() b, ok := sc.cached[id] if !ok { @@ -376,7 +392,7 @@ func (sc *SubCache[ExcerptT, CacheT, EntityT]) entityUpdated(id entity.Id) error } // evictIfNeeded will evict an entity from the cache if needed -func (sc *SubCache[ExcerptT, CacheT, EntityT]) evictIfNeeded() { +func (sc *SubCache[EntityT, ExcerptT, CacheT]) evictIfNeeded() { sc.mu.Lock() defer sc.mu.Unlock() if sc.lru.Len() <= sc.maxLoaded { diff --git a/commands/execenv/env.go b/commands/execenv/env.go index a63f835a..4b353279 100644 --- a/commands/execenv/env.go +++ b/commands/execenv/env.go @@ -128,11 +128,30 @@ func LoadBackend(env *Env) func(*cobra.Command, []string) error { return err } - env.Backend, err = cache.NewRepoCache(env.Repo) + var events chan cache.BuildEvent + env.Backend, events, err = cache.NewRepoCache(env.Repo) if err != nil { return err } + if events != nil { + _, _ = fmt.Fprintln(os.Stderr, "Building cache... ") + } + + for event := range events { + if event.Err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Cache building error [%s]: %v\n", event.Typename, event.Err) + continue + } + + switch event.Event { + case cache.BuildEventStarted: + _, _ = fmt.Fprintf(os.Stderr, "[%s] started\n", event.Typename) + case cache.BuildEventFinished: + _, _ = fmt.Fprintf(os.Stderr, "[%s] done\n", event.Typename) + } + } + cleaner := func(env *Env) interrupt.CleanerFunc { return func() error { if env.Backend != nil { diff --git a/entity/resolver.go b/entity/resolver.go index b2f831d7..9cacbf00 100644 --- a/entity/resolver.go +++ b/entity/resolver.go @@ -64,18 +64,18 @@ func (c *CachedResolver) Resolve(id Id) (Interface, error) { return i, nil } -var _ Resolver = ResolverFunc(nil) +var _ Resolver = ResolverFunc[Interface](nil) // ResolverFunc is a helper to morph a function resolver into a Resolver -type ResolverFunc func(id Id) (Interface, error) +type ResolverFunc[T Interface] func(id Id) (T, error) -func (fn ResolverFunc) Resolve(id Id) (Interface, error) { +func (fn ResolverFunc[T]) Resolve(id Id) (Interface, error) { return fn(id) } // MakeResolver create a resolver able to return the given entities. func MakeResolver(entities ...Interface) Resolver { - return ResolverFunc(func(id Id) (Interface, error) { + return ResolverFunc[Interface](func(id Id) (Interface, error) { for _, entity := range entities { if entity.Id() == id { return entity, nil |