Erstes Commit
This commit is contained in:
27
internal/formatter/json.go
Normal file
27
internal/formatter/json.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"royalroadupdates/internal/royalroad"
|
||||
)
|
||||
|
||||
// jsonEntry is the JSON output shape.
|
||||
type jsonEntry struct {
|
||||
Name string `json:"name"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
// PrintJSON writes stories as JSON to w.
|
||||
func PrintJSON(w io.Writer, stories []royalroad.Story) error {
|
||||
entries := make([]jsonEntry, 0, len(stories))
|
||||
for _, s := range stories {
|
||||
entries = append(entries, jsonEntry{
|
||||
Name: s.Name,
|
||||
Date: s.DateModified.UTC().Format("2006-01-02T15:04:05Z"),
|
||||
})
|
||||
}
|
||||
enc := json.NewEncoder(w)
|
||||
return enc.Encode(entries)
|
||||
}
|
||||
41
internal/formatter/relative.go
Normal file
41
internal/formatter/relative.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Relative returns a human-friendly relative time string like "3 hours ago".
|
||||
func Relative(t time.Time) string {
|
||||
d := time.Since(t)
|
||||
|
||||
if d < time.Minute {
|
||||
return "just now"
|
||||
}
|
||||
if d < time.Hour {
|
||||
m := int(d.Minutes())
|
||||
return pluralize(m, "minute") + " ago"
|
||||
}
|
||||
if d < 24*time.Hour {
|
||||
h := int(d.Hours())
|
||||
return pluralize(h, "hour") + " ago"
|
||||
}
|
||||
if d < 30*24*time.Hour {
|
||||
day := int(d.Hours() / 24)
|
||||
return pluralize(day, "day") + " ago"
|
||||
}
|
||||
if d < 365*24*time.Hour {
|
||||
month := int(d.Hours() / 24 / 30)
|
||||
return pluralize(month, "month") + " ago"
|
||||
}
|
||||
|
||||
year := int(d.Hours() / 24 / 365)
|
||||
return pluralize(year, "year") + " ago"
|
||||
}
|
||||
|
||||
func pluralize(n int, unit string) string {
|
||||
if n == 1 {
|
||||
return fmt.Sprintf("1 %s", unit)
|
||||
}
|
||||
return fmt.Sprintf("%d %ss", n, unit)
|
||||
}
|
||||
39
internal/formatter/relative_test.go
Normal file
39
internal/formatter/relative_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRelative(t *testing.T) {
|
||||
t.Parallel()
|
||||
now := time.Now()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
t time.Time
|
||||
want string
|
||||
}{
|
||||
{"just now", now.Add(-5 * time.Second), "just now"},
|
||||
{"1 minute ago", now.Add(-1 * time.Minute), "1 minute ago"},
|
||||
{"5 minutes ago", now.Add(-5 * time.Minute), "5 minutes ago"},
|
||||
{"1 hour ago", now.Add(-1 * time.Hour), "1 hour ago"},
|
||||
{"3 hours ago", now.Add(-3 * time.Hour), "3 hours ago"},
|
||||
{"1 day ago", now.Add(-24 * time.Hour), "1 day ago"},
|
||||
{"2 days ago", now.Add(-48 * time.Hour), "2 days ago"},
|
||||
{"1 month ago", now.Add(-30 * 24 * time.Hour), "1 month ago"},
|
||||
{"3 months ago", now.Add(-90 * 24 * time.Hour), "3 months ago"},
|
||||
{"1 year ago", now.Add(-365 * 24 * time.Hour), "1 year ago"},
|
||||
{"2 years ago", now.Add(-730 * 24 * time.Hour), "2 years ago"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := Relative(tt.t)
|
||||
if got != tt.want {
|
||||
t.Errorf("Relative() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
33
internal/formatter/table.go
Normal file
33
internal/formatter/table.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"royalroadupdates/internal/royalroad"
|
||||
)
|
||||
|
||||
// PrintTable writes a plain-text aligned table of stories to w.
|
||||
func PrintTable(w io.Writer, stories []royalroad.Story) {
|
||||
if len(stories) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
nameHeader := "Story Name"
|
||||
updateHeader := "Last Update"
|
||||
|
||||
maxNameLen := len(nameHeader)
|
||||
for _, s := range stories {
|
||||
if l := len(s.Name); l > maxNameLen {
|
||||
maxNameLen = l
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%-*s %s\n", maxNameLen, nameHeader, updateHeader)
|
||||
fmt.Fprintf(w, "%s %s\n", strings.Repeat("-", maxNameLen), strings.Repeat("-", len(updateHeader)))
|
||||
|
||||
for _, s := range stories {
|
||||
fmt.Fprintf(w, "%-*s %s\n", maxNameLen, s.Name, Relative(s.DateModified))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user