master
  1package main
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"sort"
  7	"strings"
  8
  9	"github.com/antonmedv/gitmal/pkg/git"
 10	"github.com/antonmedv/gitmal/pkg/links"
 11	"github.com/antonmedv/gitmal/pkg/templates"
 12)
 13
 14func generateIndex(files []git.Blob, params Params) error {
 15	// Build directory indexes
 16	type dirInfo struct {
 17		subdirs map[string]struct{}
 18		files   []git.Blob
 19	}
 20	dirs := map[string]*dirInfo{}
 21
 22	ensureDir := func(p string) *dirInfo {
 23		if di, ok := dirs[p]; ok {
 24			return di
 25		}
 26		di := &dirInfo{subdirs: map[string]struct{}{}, files: []git.Blob{}}
 27		dirs[p] = di
 28		return di
 29	}
 30
 31	dirsSet := links.BuildDirSet(files)
 32	filesSet := links.BuildFileSet(files)
 33
 34	for _, b := range files {
 35		// Normalize to forward slash paths for URL construction
 36		p := b.Path
 37		parts := strings.Split(p, "/")
 38		// walk directories
 39		cur := ""
 40		for i := 0; i < len(parts)-1; i++ {
 41			child := parts[i]
 42			ensureDir(cur).subdirs[child] = struct{}{}
 43			if cur == "" {
 44				cur = child
 45			} else {
 46				cur = cur + "/" + child
 47			}
 48			ensureDir(cur) // ensure it exists
 49		}
 50		ensureDir(cur).files = append(ensureDir(cur).files, b)
 51	}
 52
 53	di := dirs[""] // root
 54
 55	outDir := params.OutputDir
 56	if err := os.MkdirAll(outDir, 0o755); err != nil {
 57		return err
 58	}
 59
 60	// Build entries
 61	dirNames := make([]string, 0, len(di.subdirs))
 62	for name := range di.subdirs {
 63		dirNames = append(dirNames, name)
 64	}
 65	// Sort for stable output
 66	sort.Strings(dirNames)
 67	sort.Slice(di.files, func(i, j int) bool {
 68		return di.files[i].FileName < di.files[j].FileName
 69	})
 70
 71	subdirEntries := make([]templates.ListEntry, 0, len(dirNames))
 72	for _, name := range dirNames {
 73		subdirEntries = append(subdirEntries, templates.ListEntry{
 74			Name:  name + "/",
 75			Href:  "blob/" + params.Ref.DirName() + "/" + name + "/index.html",
 76			IsDir: true,
 77		})
 78	}
 79
 80	fileEntries := make([]templates.ListEntry, 0, len(di.files))
 81	for _, b := range di.files {
 82		fileEntries = append(fileEntries, templates.ListEntry{
 83			Name: b.FileName + "",
 84			Href: "blob/" + params.Ref.DirName() + "/" + b.FileName + ".html",
 85			Mode: b.Mode,
 86			Size: humanizeSize(b.Size),
 87		})
 88	}
 89
 90	// Title and current path label
 91	title := params.Name
 92
 93	f, err := os.Create(filepath.Join(outDir, "index.html"))
 94	if err != nil {
 95		return err
 96	}
 97
 98	rootHref := "./"
 99	readmeHTML := readme(di.files, dirsSet, filesSet, params, rootHref)
100
101	err = templates.ListTemplate.ExecuteTemplate(f, "layout.gohtml", templates.ListParams{
102		LayoutParams: templates.LayoutParams{
103			Title:         title,
104			Name:          params.Name,
105			SiteName:      params.SiteName,
106			Dark:          params.Dark,
107			CSSMarkdown:   cssMarkdown(params.Dark),
108			RootHref:      rootHref + params.RootPrefix,
109			RepoHref:      rootHref,
110			CurrentRefDir: params.Ref.DirName(),
111			Selected:      "code",
112			InlineStyles:  params.InlineStyles,
113		},
114		HeaderParams: templates.HeaderParams{
115			Ref:         params.Ref,
116			Breadcrumbs: breadcrumbs(params.Name, "", false),
117		},
118		Ref:    params.Ref,
119		Dirs:   subdirEntries,
120		Files:  fileEntries,
121		Readme: readmeHTML,
122	})
123	if err != nil {
124		_ = f.Close()
125		return err
126	}
127	if err := f.Close(); err != nil {
128		return err
129	}
130
131	return nil
132}