97 lines
3.2 KiB
Go
97 lines
3.2 KiB
Go
package main
|
|
|
|
// The mail signal's own words (novox/hq ADR 0461 §3, §4; issue 1429): a pure function, no store, no bus.
|
|
// Sending it — messenger_check, the safe form, the cap, marking mails told — is 1522.
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// SignalMail is one mail the mail signal may name (ADR 0461 §3): just enough of its entry, never a body.
|
|
// Date is the mail's own date, the "oldest first" tiebreak among mails with no due date (1429/AC1); it is
|
|
// not itself said.
|
|
type SignalMail struct {
|
|
SenderName string // "" answers "a sender without a name" (1429/AC2), never the address
|
|
Summary string
|
|
Date time.Time
|
|
DueDate time.Time // zero means none
|
|
}
|
|
|
|
// SignalWords is what one mail signal says (ADR 0461 §1): a headline, and at most two lines.
|
|
type SignalWords struct {
|
|
Headline string
|
|
Lines []string
|
|
}
|
|
|
|
// orderSignalMails is 1429/AC1, AC4: nearest due date first (one without a due date after all those with
|
|
// one), then oldest first among mails sharing that place — "mails carried from earlier (untold)" are simply
|
|
// part of the same list handed in, ordered exactly as any other mail that matters is.
|
|
func orderSignalMails(mails []SignalMail) []SignalMail {
|
|
sorted := append([]SignalMail(nil), mails...)
|
|
sort.SliceStable(sorted, func(i, j int) bool {
|
|
a, b := sorted[i], sorted[j]
|
|
aNone, bNone := a.DueDate.IsZero(), b.DueDate.IsZero()
|
|
if aNone != bNone {
|
|
return bNone // a has a due date, b does not: a first
|
|
}
|
|
if !aNone {
|
|
return a.DueDate.Before(b.DueDate)
|
|
}
|
|
return a.Date.Before(b.Date) // oldest first, among mails with no due date
|
|
})
|
|
return sorted
|
|
}
|
|
|
|
// headline is "1 mail matters" or "N mails matter" (1429/AC1, AC3: the same headline whether the words are
|
|
// said in full or the safe form).
|
|
func headline(n int) string {
|
|
if n == 1 {
|
|
return "1 mail matters"
|
|
}
|
|
return fmt.Sprintf("%d mails matter", n)
|
|
}
|
|
|
|
// mailLine is one mail's own line (1429/AC1): "<sender's name> — <summary> — due <date>", the due part left
|
|
// out entirely when there is none; a sender with no display name is "a sender without a name" (AC2), never
|
|
// its address.
|
|
func mailLine(m SignalMail) string {
|
|
sender := m.SenderName
|
|
if sender == "" {
|
|
sender = "a sender without a name"
|
|
}
|
|
line := sender + " — " + m.Summary
|
|
if !m.DueDate.IsZero() {
|
|
line += " — due " + m.DueDate.Format("2006-01-02")
|
|
}
|
|
return line
|
|
}
|
|
|
|
// BuildSignal is the mail signal's words in full (1429/AC1, AC2, AC4): the headline, the nearest-due mail's
|
|
// own line, and — with more than one — the next one's, ending ", and N-2 more on the mail page" once there
|
|
// are more than two in all.
|
|
func BuildSignal(mails []SignalMail) SignalWords {
|
|
ordered := orderSignalMails(mails)
|
|
n := len(ordered)
|
|
w := SignalWords{Headline: headline(n)}
|
|
if n == 0 {
|
|
return w
|
|
}
|
|
w.Lines = append(w.Lines, mailLine(ordered[0]))
|
|
if n >= 2 {
|
|
second := mailLine(ordered[1])
|
|
if n > 2 {
|
|
second += fmt.Sprintf(", and %d more on the mail page", n-2)
|
|
}
|
|
w.Lines = append(w.Lines, second)
|
|
}
|
|
return w
|
|
}
|
|
|
|
// SafeSignal is 1429/AC3: the safe form a refused set of words falls back to — the same headline, one line
|
|
// naming nothing of any mail.
|
|
func SafeSignal(n int) SignalWords {
|
|
return SignalWords{Headline: headline(n), Lines: []string{"open the mail page"}}
|
|
}
|