Go · 2432 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package repo
4
5 import (
6 "context"
7 "net/url"
8 "strings"
9
10 repogit "github.com/tenseleyFlow/shithub/internal/repos/git"
11 )
12
13 type codeTreeEntryRow struct {
14 Entry repogit.TreeEntry
15 FullPath string
16 URL string
17 LastCommit repogit.Commit
18 LastFound bool
19 }
20
21 func (h *Handlers) codeTreeEntryRows(ctx context.Context, cc *codeContext, entries []repogit.TreeEntry) []codeTreeEntryRow {
22 submodules := map[string]repogit.Submodule{}
23 if hasSubmoduleEntries(entries) {
24 var err error
25 submodules, err = repogit.Submodules(ctx, cc.gitDir, cc.ref)
26 if err != nil && h.d.Logger != nil {
27 h.d.Logger.WarnContext(ctx, "code: submodules", "error", err)
28 submodules = map[string]repogit.Submodule{}
29 }
30 }
31
32 rows := make([]codeTreeEntryRow, 0, len(entries))
33 for _, e := range entries {
34 fullPath := joinPath(cc.subpath, e.Name)
35 entryURL := treeEntryURL(cc.owner, cc.row.Name, cc.ref, e.Kind, fullPath)
36 if e.Kind == repogit.EntrySubmod {
37 entryURL = ""
38 if sm, ok := submodules[fullPath]; ok {
39 entryURL = h.submoduleTreeURL(ctx, cc, sm.URL, e.OID)
40 }
41 }
42 row := codeTreeEntryRow{
43 Entry: e,
44 FullPath: fullPath,
45 URL: entryURL,
46 }
47 if commits, err := repogit.Log(ctx, cc.gitDir, repogit.LogOptions{
48 Ref: cc.ref,
49 MaxCount: 1,
50 Path: fullPath,
51 }); err == nil && len(commits) > 0 {
52 row.LastCommit = commits[0]
53 row.LastFound = true
54 } else if err != nil && h.d.Logger != nil {
55 h.d.Logger.WarnContext(ctx, "code: row history", "error", err, "path", fullPath)
56 }
57 rows = append(rows, row)
58 }
59 return rows
60 }
61
62 func hasSubmoduleEntries(entries []repogit.TreeEntry) bool {
63 for _, e := range entries {
64 if e.Kind == repogit.EntrySubmod {
65 return true
66 }
67 }
68 return false
69 }
70
71 func treeEntryURL(owner, repoName, ref string, kind repogit.TreeEntryKind, fullPath string) string {
72 base := "/" + url.PathEscape(owner) + "/" + url.PathEscape(repoName)
73 refPath := escapePathSegments(ref)
74 switch kind {
75 case repogit.EntryTree:
76 return base + "/tree/" + refPath + "/" + escapePathSegments(fullPath)
77 case repogit.EntryBlob:
78 return base + "/blob/" + refPath + "/" + escapePathSegments(fullPath)
79 default:
80 return ""
81 }
82 }
83
84 func escapePathSegments(p string) string {
85 if p == "" {
86 return ""
87 }
88 parts := strings.Split(p, "/")
89 for i := range parts {
90 parts[i] = url.PathEscape(parts[i])
91 }
92 return strings.Join(parts, "/")
93 }
94