package main import ( "context" _ "embed" "encoding/json" "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") }, "json": func(foo string, args ...any) (any, error) { switch foo { case "Marshal": b, err := json.Marshal(args[0]) return string(b), err } return nil, errors.New("not impl") }, }) 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 } eventIDs, err := s.EventsSince(ctx, t) if err != nil { return err } type aThread struct { Thread string Messages []Message First Message Last Message } type anEvent struct { Event string Threads []aThread First Message Last Message } type someEvents struct { Events []anEvent } return tmpl.Execute(w, map[string]any{ "since": t.Format("2006-01-02"), "events": func() someEvents { events := make([]anEvent, len(eventIDs)) for i, event := range eventIDs { events[i] = func() anEvent { threadNames := []string{} for _, m := range messages { if m.Event == event { threadNames = append(threadNames, m.Thread) } } slices.Sort(threadNames) slices.Compact(threadNames) threads := make([]aThread, len(threadNames)) for i, thread := range threadNames { threads[i] = func() aThread { someMessages := []Message{} for _, m := range messages { if m.Thread == thread { someMessages = append(someMessages, m) } } sort.Slice(someMessages, func(i, j int) bool { return someMessages[i].TS < someMessages[j].TS }) return aThread{ Thread: thread, Messages: someMessages, First: func() Message { if len(someMessages) == 0 { return Message{} } return someMessages[0] }(), Last: func() Message { if len(someMessages) == 0 { return Message{} } return someMessages[len(someMessages)-1] }(), } }() } sort.Slice(threads, func(i, j int) bool { return threads[i].First.TS < threads[j].First.TS }) return anEvent{ Event: event, Threads: threads, First: func() Message { if len(threads) == 0 { return Message{} } return threads[0].First }(), Last: func() Message { if len(threads) == 0 { return Message{} } return threads[len(threads)-1].Last }(), } }() } return someEvents{ Events: events, } }(), "messages": messages, "eventNames": eventNames, }) }