main
1package generator
2
3import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "strings"
8 "time"
9
10 "mokhan.ca/xlgmokha/gitmal/internal/git"
11 "mokhan.ca/xlgmokha/gitmal/internal/templates"
12)
13
14func currentYear() int {
15 return time.Now().Year()
16}
17
18const Dot = "·"
19
20func Echo(a ...any) {
21 _, _ = fmt.Fprintln(os.Stderr, a...)
22}
23
24func breadcrumbs(rootName string, path string, isFile bool) []templates.Breadcrumb {
25 // Root list
26 if path == "" {
27 return []templates.Breadcrumb{
28 {
29 Name: rootName,
30 Href: "./index.html",
31 IsDir: true,
32 },
33 }
34 }
35
36 // Paths from git are already with '/'
37 parts := strings.Split(path, "/")
38
39 // Build breadcrumbs relative to the file location so links work in static output
40 // Example: for a/b/c.txt, at /blob/<ref>/a/b/c.txt.html
41 // - root: ../../index.html
42 // - a: ../index.html
43 // - b: index.html
44 // - c.txt: (no link)
45 d := len(parts)
46
47 // current directory depth relative to ref
48 if isFile {
49 d -= 1
50 }
51
52 crumbs := make([]templates.Breadcrumb, 0, len(parts))
53
54 // root
55 crumbs = append(crumbs, templates.Breadcrumb{
56 Name: rootName,
57 Href: "./" + strings.Repeat("../", d) + "index.html",
58 IsDir: true,
59 })
60
61 // intermediate directories
62 for i := 0; i < len(parts)-1; i++ {
63 name := parts[i]
64 // target directory depth t = i+1
65 up := d - (i + 1)
66 href := "./" + strings.Repeat("../", up) + "index.html"
67 crumbs = append(crumbs, templates.Breadcrumb{
68 Name: name,
69 Href: href,
70 IsDir: true,
71 })
72 }
73
74 // final file (no link)
75 crumbs = append(crumbs, templates.Breadcrumb{
76 Name: parts[len(parts)-1],
77 IsDir: !isFile,
78 })
79
80 return crumbs
81}
82
83func humanizeSize(size int64) string {
84 const unit = 1024
85 if size < unit {
86 return fmt.Sprintf("%d B", size)
87 }
88 div, exp := int64(unit), 0
89 for n := size / unit; n >= unit; n /= unit {
90 div *= unit
91 exp++
92 }
93 return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp])
94}
95
96func isMarkdown(path string) bool {
97 lower := strings.ToLower(path)
98 if strings.HasSuffix(lower, ".md") || strings.HasSuffix(lower, ".markdown") || strings.HasSuffix(lower, ".mdown") || strings.HasSuffix(lower, ".mkd") || strings.HasSuffix(lower, ".mkdown") {
99 return true
100 }
101 return false
102}
103
104func isImage(path string) bool {
105 switch filepath.Ext(path) {
106 case ".png", ".jpg", ".jpeg", ".gif", ".webp":
107 return true
108 default:
109 return false
110 }
111}
112
113func ContainsBranch(branches []git.Ref, branch string) bool {
114 for _, b := range branches {
115 if b.String() == branch {
116 return true
117 }
118 }
119 return false
120}
121
122func HasConflictingBranchNames(branches []git.Ref) (bool, git.Ref, git.Ref) {
123 uniq := make(map[string]git.Ref, len(branches))
124 for _, b := range branches {
125 if a, exists := uniq[b.DirName()]; exists {
126 return true, a, b
127 }
128 uniq[b.DirName()] = b
129 }
130 return false, git.Ref{}, git.Ref{}
131}