Files
royalroadupdates/internal/formatter/table.go

64 lines
1.6 KiB
Go

package formatter
import (
"fmt"
"io"
"strings"
"unicode/utf8"
"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"
// 1. Measure natural widths in runes
maxNameLen := utf8.RuneCountInString(nameHeader)
maxUpdateLen := utf8.RuneCountInString(updateHeader)
for _, s := range stories {
if l := utf8.RuneCountInString(s.Name); l > maxNameLen {
maxNameLen = l
}
if l := utf8.RuneCountInString(Relative(s.DateModified)); l > maxUpdateLen {
maxUpdateLen = l
}
}
// 2. Determine final name column width based on terminal availability
nameWidth := maxNameLen
const minNameWidth = 30
const gutter = 2
termWidth, err := getTerminalWidth()
if err == nil {
// We have a terminal. check if it's wide enough to support a cap above the min.
if termWidth >= minNameWidth+gutter+maxUpdateLen {
availableForName := termWidth - gutter - maxUpdateLen
if nameWidth > availableForName {
nameWidth = availableForName
}
}
}
// 3. Render Table
fmt.Fprintf(w, "%-*s %s\n", nameWidth, nameHeader, updateHeader)
fmt.Fprintf(w, "%s %s\n", strings.Repeat("-", nameWidth), strings.Repeat("-", maxUpdateLen))
for _, s := range stories {
displayName := s.Name
runeName := []rune(displayName)
if len(runeName) > nameWidth {
// Truncate to (nameWidth - 3) and add ellipsis
displayName = string(runeName[:nameWidth-3]) + "..."
}
fmt.Fprintf(w, "%-*s %s\n", nameWidth, displayName, Relative(s.DateModified))
}
}