-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuilder.go
95 lines (83 loc) · 2.41 KB
/
builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package cs
import (
"fmt"
"io"
"io/fs"
"log"
"maps"
"os"
"strings"
"sgrankin.dev/cs/codesearch/index"
)
type RepoFile interface {
Path() string
Size() int
FileMode() fs.FileMode
Reader() io.ReadCloser
}
type Repo interface {
// Name is the unique ref name this Repo tracks in the underlying repository.
Name() string
// For git, Version yields the SHA1 of the commit.
Version() Version
// Files yields all of the files in the tree at the current Version.
// For git, Refresh may be called concurrently, unless it deletes underlying objects.
Files(yield func(RepoFile) error) error
// Refresh fetches the remote and updates the local tracking ref.
Refresh() (Version, error)
}
type indexBuilder struct{}
func newIndexBuilder() *indexBuilder {
return &indexBuilder{}
}
// BuildIndex creates a new index at `path` with the given `repos`.
func BuildIndex(path string, repos []Repo) error {
log.Printf("Building index at %q", path)
wix := index.Create(path)
for _, repo := range repos {
if err := repo.Files(func(rf RepoFile) error {
r := rf.Reader()
defer r.Close()
wix.Add(rf.Path(), r)
return nil
}); err != nil {
wix.Close()
return fmt.Errorf("failed building index: %w", err)
}
}
wix.Flush()
wix.Close()
return nil
}
// rebuildIfNeeded creates a new index in place of the old one if it needs to be updated.
//
// The `paths` are used to check if updates are needed, returning nil if not.
//
// To check if updates are needed, repos are compared by version to what's currently in the index
// The new index is placed where the existing index resides, but the actual Index is not closed
// and can continue to be used until the new index is swapped in.
func (b *indexBuilder) rebuildIfNeeded(ix *index.Index, repos []Repo) *index.Index {
versions := map[string]Version{}
for _, p := range ix.Paths() {
repo, version, _ := strings.Cut(p, "@")
versions[repo] = version
}
newVersions := map[string]Version{}
for _, repo := range repos {
newVersions[repo.Name()] = repo.Version()
}
if maps.Equal(versions, newVersions) {
return nil
}
return b.rebuild(ix, repos)
}
func (b *indexBuilder) rebuild(ix *index.Index, repos []Repo) *index.Index {
log.Printf("Rebuilding index %q", ix.Path)
tempIndexPath := ix.Path + "~"
if err := BuildIndex(tempIndexPath, repos); err != nil {
log.Printf("Rebuild failed: %v", err)
return nil
}
os.Rename(tempIndexPath, ix.Path)
return index.Open(ix.Path)
}