package web
import (
"fmt"
"html/template"
"io"
"strings"
"time"
assets "github.com/mattnite/cairn/web"
)
var funcMap = template.FuncMap{
"timeAgo": func(t time.Time) string {
d := time.Since(t)
switch {
case d < time.Minute:
return "just now"
case d < time.Hour:
return fmt.Sprintf("%dm ago", int(d.Minutes()))
case d < 24*time.Hour:
return fmt.Sprintf("%dh ago", int(d.Hours()))
default:
return fmt.Sprintf("%dd ago", int(d.Hours()/24))
}
},
"shortSHA": func(sha string) string {
if len(sha) > 8 {
return sha[:8]
}
return sha
},
"formatSize": func(size int64) string {
switch {
case size < 1024:
return fmt.Sprintf("%d B", size)
case size < 1024*1024:
return fmt.Sprintf("%.1f KB", float64(size)/1024)
default:
return fmt.Sprintf("%.1f MB", float64(size)/(1024*1024))
}
},
"deref": func(s *string) string {
if s == nil {
return ""
}
return *s
},
"truncate": func(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n] + "..."
},
"join": strings.Join,
"derefTime": func(t *time.Time) time.Time {
if t == nil {
return time.Time{}
}
return *t
},
}
type Templates struct {
layout *template.Template
pages map[string]*template.Template
}
func LoadTemplates() (*Templates, error) {
layout, err := template.New("layout").Funcs(funcMap).ParseFS(assets.Assets, "templates/layout.html")
if err != nil {
return nil, fmt.Errorf("parsing layout: %w", err)
}
pageFiles := []string{
"templates/pages/index.html",
"templates/pages/artifacts.html",
"templates/pages/artifact_detail.html",
"templates/pages/repos.html",
"templates/pages/crashgroups.html",
"templates/pages/crashgroup_detail.html",
"templates/pages/search.html",
"templates/pages/regression.html",
"templates/pages/targets.html",
"templates/pages/target_detail.html",
"templates/pages/run_detail.html",
}
pages := map[string]*template.Template{}
for _, pf := range pageFiles {
name := strings.TrimPrefix(pf, "templates/pages/")
name = strings.TrimSuffix(name, ".html")
t, err := template.Must(layout.Clone()).ParseFS(assets.Assets, pf)
if err != nil {
return nil, fmt.Errorf("parsing page %s: %w", name, err)
}
pages[name] = t
}
return &Templates{layout: layout, pages: pages}, nil
}
func (t *Templates) Render(w io.Writer, page string, data any) error {
tmpl, ok := t.pages[page]
if !ok {
return fmt.Errorf("template %q not found", page)
}
return tmpl.ExecuteTemplate(w, "layout", data)
}