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}