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