package main import ( "context" _ "embed" "errors" "io" "slices" "sort" "text/template" "time" ) //go:embed report.tmpl var reportTMPL string func ReportSince(ctx context.Context, w io.Writer, s Storage, t time.Time) error { tmpl := template.New("report").Funcs(map[string]any{ "time": func(foo string, args ...any) (any, error) { switch foo { case "Unix": seconds, _ := args[0].(uint64) return time.Unix(int64(seconds), 0), nil case "Time.Format": t, _ := args[1].(time.Time) return t.Format(args[0].(string)), nil } return nil, errors.New("not impl") }, "null": func() any { return nil }, "append": func(arr any, v any) (any, error) { if v == nil { return arr, nil } switch arr := arr.(type) { case []any: return append(arr, v), nil case nil: return []any{v}, nil } return nil, errors.New("unknown type in append") }, }) tmpl, err := tmpl.Parse(reportTMPL) if err != nil { return err } messages, err := s.MessagesSince(ctx, t) if err != nil { return err } eventNames, err := s.EventNamesSince(ctx, t) if err != nil { return err } events, err := s.EventsSince(ctx, t) if err != nil { return err } return tmpl.Execute(w, map[string]any{ "since": t.Format("2006-01-02"), "events": func() any { threads := make([]any, len(events)) for i, event := range events { threads[i] = func() any { threadNames := []string{} for _, m := range messages { if m.Event == event { threadNames = append(threadNames, m.Thread) } } slices.Sort(threadNames) slices.Compact(threadNames) threads := make([]any, len(threadNames)) for i, thread := range threadNames { threads[i] = func() any { messages := []Message{} for _, m := range messages { if m.Thread == thread { messages = append(messages, m) } } sort.Slice(messages, func(i, j int) bool { return messages[i].TS < messages[j].TS }) return map[string]any{ "thread": thread, "messages": messages, "first": func() any { if len(messages) == 0 { return Message{} } return messages[0] }(), "last": func() any { if len(messages) == 0 { return Message{} } return messages[len(messages)-1] }(), } }() } sort.Slice(threads, func(i, j int) bool { return threads[i].(map[string]any)["first"].(Message).TS < threads[j].(map[string]any)["first"].(Message).TS }) return map[string]any{ "event": event, "threads": threads, "first": func() any { if len(threads) == 0 { return Message{} } return threads[0].(map[string]any)["first"] }(), "last": func() any { if len(threads) == 0 { return Message{} } return threads[len(threads)-1].(map[string]any)["last"] }(), } }() } return map[string]any{ "threads": threads, } }(), "messages": messages, "eventNames": eventNames, }) }