aboutsummaryrefslogtreecommitdiff
path: root/internal/render/table.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/render/table.go')
-rw-r--r--internal/render/table.go66
1 files changed, 65 insertions, 1 deletions
diff --git a/internal/render/table.go b/internal/render/table.go
index 3ade296..3219e9d 100644
--- a/internal/render/table.go
+++ b/internal/render/table.go
@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
+ "github.com/lukaszkasprzak/prognosis/internal/config"
"github.com/lukaszkasprzak/prognosis/internal/openmeteo"
)
@@ -14,8 +15,34 @@ type cell struct {
left bool
}
+// custom returns the config's definition of a column, if it has one.
+func (x ctx) custom(name string) (config.CustomColumn, bool) {
+ cc, ok := x.cfg.Custom[name]
+ return cc, ok
+}
+
+// customLabel is the header for a custom column: what the user asked for, or
+// the column's own name.
+func customLabel(name string, cc config.CustomColumn) string {
+ if cc.Label != "" {
+ return cc.Label
+ }
+ return name
+}
+
// colWidth is the reserved display width per column.
func (x ctx) colWidth(name string) int {
+ if cc, ok := x.custom(name); ok {
+ if cc.Width > 0 {
+ return cc.Width
+ }
+ // Wide enough for the header, and for a value of a few digits.
+ w := DisplayWidth(customLabel(name, cc))
+ if w < 5 {
+ w = 5
+ }
+ return w
+ }
switch name {
case "hour":
// Three, not two: the Python leaves a double space after the hour.
@@ -39,6 +66,9 @@ func (x ctx) colWidth(name string) int {
}
func (x ctx) leftAligned(name string) bool {
+ if _, ok := x.custom(name); ok {
+ return false // custom columns are numeric
+ }
switch name {
case "hour", "icon", "temp", "feels", "conditions":
return true
@@ -86,7 +116,12 @@ func (x ctx) table(v View) []string {
headers := map[string]cell{}
for _, name := range x.visible() {
- headers[name] = cell{text: x.cat.Header(name), style: ""}
+ text := x.cat.Header(name)
+ if cc, ok := x.custom(name); ok {
+ // A user-declared label is not ours to translate.
+ text = customLabel(name, cc)
+ }
+ headers[name] = cell{text: text, style: ""}
}
out = append(out, x.c(Underline, x.rowPlain(headers)))
@@ -129,6 +164,10 @@ func (x ctx) cells(r openmeteo.Row, isNow bool, prevCode *int) map[string]cell {
temp, hasTemp := r.Val("temperature_2m")
for _, name := range x.visible() {
+ if cc, ok := x.custom(name); ok {
+ out[name] = x.customCell(r, name, cc)
+ continue
+ }
switch name {
case "hour":
style := Reset
@@ -238,3 +277,28 @@ func abs(f float64) float64 {
}
return f
}
+
+// CustomKey is where a custom column's value lives in a Row.
+//
+// Prefixed so a column called "temp" or "visibility" can never shadow the API
+// field of the same name that a built-in column reads.
+func CustomKey(name string) string { return "x:" + name }
+
+// customCell formats one user-declared column.
+//
+// A value the API did not supply renders blank rather than as zero: for an
+// allergen or a soil reading, "no data" and "none" are different claims.
+func (x ctx) customCell(r openmeteo.Row, name string, cc config.CustomColumn) cell {
+ key := cc.Field
+ if cc.Source == "air" {
+ key = CustomKey(name)
+ }
+ v, ok := r.Val(key)
+ if !ok {
+ return cell{text: "", style: Dim}
+ }
+ return cell{
+ text: fmt.Sprintf("%.*f%s", cc.Decimals, v, cc.Suffix),
+ style: Dim,
+ }
+}