coverage~bigbes/sr-ht-ecore3bd158fbchrome/funcs.go

Coverage
96.4% 27/28 statements
Δ
Blob
773a227
Uncovered L89-L90
1 package chrome
2
3 import (
4 "fmt"
5 "html/template"
6 "time"
7 )
8
9 // Funcs returns the template helpers every service was carrying its own copy
10 // of. Merge into a service's FuncMap before its own helpers, so a service can
11 // still shadow a name deliberately.
12 19 func Funcs() template.FuncMap {
13 19 return template.FuncMap{
14 19 "dict": Dict,
15 19 "shortsha": ShortSHA,
16 19 "reltime": RelTime,
17 19 "abstime": AbsTime,
18 19 }
19 19 }
20
21 // Dict builds a map from alternating key/value arguments, so a partial that
22 // needs several fields can be invoked with an inline context:
23 // {{template "x" (dict "A" .A "B" .B)}}. An odd argument count or a non-string
24 // key is a template authoring error and surfaces as a render error.
25 3 func Dict(kv ...any) (map[string]any, error) {
26 3 if len(kv)%2 != 0 {
27 1 return nil, fmt.Errorf("dict: expected an even number of arguments, got %d", len(kv))
28 1 }
29 2 m := make(map[string]any, len(kv)/2)
30 3 for i := 0; i < len(kv); i += 2 {
31 3 k, ok := kv[i].(string)
32 3 if !ok {
33 1 return nil, fmt.Errorf("dict: key %d is not a string", i)
34 1 }
35 2 m[k] = kv[i+1]
36 }
37 1 return m, nil
38 }
39
40 // ShortSHA abbreviates an object id to its first 8 characters (or returns it
41 // unchanged if shorter), the convention used everywhere commits are listed.
42 2 func ShortSHA(s string) string {
43 2 if len(s) > 8 {
44 1 return s[:8]
45 1 }
46 1 return s
47 }
48
49 // RelTime is the coarse "3 hours ago" a listing wants, and AbsTime the exact
50 // UTC stamp an investigation wants. Both exist, and both are shared, because
51 // every service on the instance shows the same two columns and had grown its
52 // own spelling of them: the copies disagreed about the future, printing "in 3
53 // hours" on one service and "just now" on the next for the same instant.
54 //
55 // A future instant gets the same arithmetic as a past one. "in 3 weeks"
56 // answers "do I have to deal with this today" without the reader working it
57 // out from a calendar stamp.
58 8 func RelTime(t time.Time) string {
59 8 d := time.Since(t)
60 8 switch {
61 1 case d < -time.Minute:
62 1 return "in " + coarse(-d)
63 2 case d < time.Minute:
64 2 // Covers both an instant that has just passed and one about to, which
65 2 // is also what two machines with unsynchronised clocks produce for the
66 2 // same instant.
67 2 return "just now"
68 5 default:
69 5 return coarse(d) + " ago"
70 }
71 }
72
73 // AbsTime is the unambiguous stamp, one hover away from a RelTime.
74 4 func AbsTime(t time.Time) string {
75 4 return t.UTC().Format("2006-01-02 15:04:05 UTC")
76 4 }
77
78 // coarse names a positive duration in its largest whole unit. The direction is
79 // the caller's to add, so that "in 3 weeks" and "3 weeks ago" cannot end up
80 // counting in different units.
81 6 func coarse(d time.Duration) string {
82 6 switch {
83 1 case d < time.Hour:
84 1 return plural(int(d/time.Minute), "minute")
85 3 case d < 24*time.Hour:
86 3 return plural(int(d/time.Hour), "hour")
87 1 case d < 30*24*time.Hour:
88 1 return plural(int(d/(24*time.Hour)), "day")
89 0 case d < 365*24*time.Hour:
90 0 return plural(int(d/(30*24*time.Hour)), "month")
91 1 default:
92 1 return plural(int(d/(365*24*time.Hour)), "year")
93 }
94 }
95
96 6 func plural(n int, unit string) string {
97 6 if n == 1 {
98 1 return "1 " + unit
99 1 }
100 5 return fmt.Sprintf("%d %ss", n, unit)
101 }