1package repo
2
3import (
4 "context"
5 "crypto/rand"
6 "fmt"
7 "math/big"
8 "slices"
9 "sort"
10 "strings"
11
12 "tangled.org/core/appview/db"
13 "tangled.org/core/appview/models"
14 "tangled.org/core/appview/pages/repoinfo"
15 "tangled.org/core/types"
16
17 "github.com/go-git/go-git/v5/plumbing/object"
18)
19
20func sortFiles(files []types.NiceTree) {
21 sort.Slice(files, func(i, j int) bool {
22 iIsFile := files[i].IsFile
23 jIsFile := files[j].IsFile
24 if iIsFile != jIsFile {
25 return !iIsFile
26 }
27 return files[i].Name < files[j].Name
28 })
29}
30
31func sortBranches(branches []types.Branch) {
32 slices.SortFunc(branches, func(a, b types.Branch) int {
33 if a.IsDefault {
34 return -1
35 }
36 if b.IsDefault {
37 return 1
38 }
39 if a.Commit != nil && b.Commit != nil {
40 if a.Commit.Committer.When.Before(b.Commit.Committer.When) {
41 return 1
42 } else {
43 return -1
44 }
45 }
46 return strings.Compare(a.Name, b.Name)
47 })
48}
49
50func uniqueEmails(commits []*object.Commit) []string {
51 emails := make(map[string]struct{})
52 for _, commit := range commits {
53 if commit.Author.Email != "" {
54 emails[commit.Author.Email] = struct{}{}
55 }
56 if commit.Committer.Email != "" {
57 emails[commit.Committer.Email] = struct{}{}
58 }
59 }
60 var uniqueEmails []string
61 for email := range emails {
62 uniqueEmails = append(uniqueEmails, email)
63 }
64 return uniqueEmails
65}
66
67func balanceIndexItems(commitCount, branchCount, tagCount, fileCount int) (commitsTrunc int, branchesTrunc int, tagsTrunc int) {
68 if commitCount == 0 && tagCount == 0 && branchCount == 0 {
69 return
70 }
71
72 // typically 1 item on right side = 2 files in height
73 availableSpace := fileCount / 2
74
75 // clamp tagcount
76 if tagCount > 0 {
77 tagsTrunc = 1
78 availableSpace -= 1 // an extra subtracted for headers etc.
79 }
80
81 // clamp branchcount
82 if branchCount > 0 {
83 branchesTrunc = min(max(branchCount, 1), 3)
84 availableSpace -= branchesTrunc // an extra subtracted for headers etc.
85 }
86
87 // show
88 if commitCount > 0 {
89 commitsTrunc = max(availableSpace, 3)
90 }
91
92 return
93}
94
95// emailToDidOrHandle takes an emailToDidMap from db.GetEmailToDid
96// and resolves all dids to handles and returns a new map[string]string
97func emailToDidOrHandle(r *Repo, emailToDidMap map[string]string) map[string]string {
98 if emailToDidMap == nil {
99 return nil
100 }
101
102 var dids []string
103 for _, v := range emailToDidMap {
104 dids = append(dids, v)
105 }
106 resolvedIdents := r.idResolver.ResolveIdents(context.Background(), dids)
107
108 didHandleMap := make(map[string]string)
109 for _, identity := range resolvedIdents {
110 if !identity.Handle.IsInvalidHandle() {
111 didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
112 } else {
113 didHandleMap[identity.DID.String()] = identity.DID.String()
114 }
115 }
116
117 // Create map of email to didOrHandle for commit display
118 emailToDidOrHandle := make(map[string]string)
119 for email, did := range emailToDidMap {
120 if didOrHandle, ok := didHandleMap[did]; ok {
121 emailToDidOrHandle[email] = didOrHandle
122 }
123 }
124
125 return emailToDidOrHandle
126}
127
128func randomString(n int) string {
129 const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
130 result := make([]byte, n)
131
132 for i := 0; i < n; i++ {
133 n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
134 result[i] = letters[n.Int64()]
135 }
136
137 return string(result)
138}
139
140// grab pipelines from DB and munge that into a hashmap with commit sha as key
141//
142// golang is so blessed that it requires 35 lines of imperative code for this
143func getPipelineStatuses(
144 d *db.DB,
145 repoInfo repoinfo.RepoInfo,
146 shas []string,
147) (map[string]models.Pipeline, error) {
148 m := make(map[string]models.Pipeline)
149
150 if len(shas) == 0 {
151 return m, nil
152 }
153
154 ps, err := db.GetPipelineStatuses(
155 d,
156 db.FilterEq("repo_owner", repoInfo.OwnerDid),
157 db.FilterEq("repo_name", repoInfo.Name),
158 db.FilterEq("knot", repoInfo.Knot),
159 db.FilterIn("sha", shas),
160 )
161 if err != nil {
162 return nil, err
163 }
164
165 for _, p := range ps {
166 m[p.Sha] = p
167 }
168
169 return m, nil
170}