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