1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
package server
import (
"fmt"
"html"
"html/template"
"io/fs"
"log/slog"
"path"
"regexp"
"searchix/frontend"
"searchix/internal/config"
"searchix/internal/nix"
"strings"
"github.com/pkg/errors"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
)
type TemplateCollection map[string]*template.Template
var (
md = goldmark.New(
goldmark.WithExtensions(extension.NewLinkify()),
)
firstSentenceRegexp = regexp.MustCompile(`^.*?\.[[:space:]]`)
)
var templateFuncs = template.FuncMap{
"firstSentence": func(input nix.Markdown) nix.Markdown {
if fs := firstSentenceRegexp.FindString(string(input)); fs != "" {
return nix.Markdown(fs)
}
return input
},
"markdown": func(input nix.Markdown) template.HTML {
var out strings.Builder
err := md.Convert([]byte(input), &out)
if err != nil {
slog.Warn(fmt.Sprintf("markdown conversion failed: %v", err))
return template.HTML(html.EscapeString(err.Error())) // #nosec G203 -- duh?
}
return template.HTML(out.String()) // #nosec G203
},
"sourceNameAndType": func(source config.Source) (string, error) {
switch source.Importer {
case config.Options:
return source.Name + " " + source.Importer.String(), nil
case config.Packages:
return source.Name, nil
default:
return "", errors.Errorf("unknown source importer type %s", source.Importer.String())
}
},
"sourceName": func(input string) string {
switch input {
case "nixos":
return "NixOS"
case "darwin":
return "Darwin"
case "home-manager":
return "Home Manager"
}
return input
},
}
func loadTemplate(
layoutFile string,
filenames ...string,
) (*template.Template, error) {
tpl := template.New("index.gotmpl")
tpl.Funcs(templateFuncs)
_, err := tpl.ParseFS(frontend.Files, layoutFile)
if err != nil {
return nil, errors.WithMessage(err, "could not parse layout template")
}
if len(filenames) > 0 {
_, err = tpl.ParseFS(frontend.Files, filenames...)
if err != nil {
return nil, errors.WithMessage(err, "could not parse template")
}
}
return tpl, nil
}
func loadTemplates() (TemplateCollection, error) {
templateDir := "templates"
templates := make(TemplateCollection, 0)
layoutFile := path.Join(templateDir, "index.gotmpl")
index, err := loadTemplate(layoutFile)
if err != nil {
return nil, err
}
templates["index"] = index
glob := path.Join(templateDir, "*.gotmpl")
templatePaths, err := fs.Glob(frontend.Files, glob)
if err != nil {
return nil, errors.WithMessage(err, "could not glob main templates")
}
for _, fullname := range templatePaths {
tpl, err := loadTemplate(layoutFile, fullname)
if err != nil {
return nil, err
}
name, _ := strings.CutSuffix(path.Base(fullname), ".gotmpl")
templates[name] = tpl
}
glob = path.Join(templateDir, "blocks", "*.gotmpl")
templatePaths, err = fs.Glob(frontend.Files, glob)
if err != nil {
return nil, errors.WithMessage(err, "could not glob block templates")
}
for _, fullname := range templatePaths {
tpl, err := loadTemplate(layoutFile, glob, fullname)
if err != nil {
return nil, err
}
name, _ := strings.CutSuffix(path.Base(fullname), ".gotmpl")
templates[name] = tpl
}
return templates, nil
}
|