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/campaigns.html", "templates/pages/campaign_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) }