diff options
Diffstat (limited to 'cmd/prognosis')
| -rw-r--r-- | cmd/prognosis/args_test.go | 2 | ||||
| -rw-r--r-- | cmd/prognosis/main.go | 94 |
2 files changed, 60 insertions, 36 deletions
diff --git a/cmd/prognosis/args_test.go b/cmd/prognosis/args_test.go index 4a85a2d..32a4208 100644 --- a/cmd/prognosis/args_test.go +++ b/cmd/prognosis/args_test.go @@ -41,7 +41,7 @@ func TestPositionalsJoinIntoThePlace(t *testing.T) { func TestNoPlaceAnywhereIsNotAnError(t *testing.T) { got, err := placeFromArgs("", nil) if err != nil { - t.Fatalf("unexpected error %v: the config and ~/.wegorc are consulted next", err) + t.Fatalf("unexpected error %v: location= in the config is consulted next", err) } if got != "" { t.Errorf("got %q, want empty", got) diff --git a/cmd/prognosis/main.go b/cmd/prognosis/main.go index 6e29c8e..3075563 100644 --- a/cmd/prognosis/main.go +++ b/cmd/prognosis/main.go @@ -3,7 +3,6 @@ package main import ( - "bufio" "errors" "flag" "fmt" @@ -19,7 +18,9 @@ import ( "github.com/lukaszkasprzak/prognosis/internal/render" ) -const wegorc = ".wegorc" +// version is stamped at build time: -ldflags "-X main.version=$(git describe)". +// "dev" means someone built it straight from a working tree. +var version = "dev" func main() { os.Exit(run()) } @@ -28,12 +29,13 @@ func run() int { configureResolver() var ( - location = flag.String("l", "", "place to query (default: config, then ~/.wegorc)") + location = flag.String("l", "", "place to query (default: location= in the config file)") hours = flag.Int("n", 0, "hours ahead to show") days = flag.Int("d", 0, "days ahead to show, 24h each") columns = flag.String("columns", "", "comma-separated columns to display") icons = flag.String("icons", "", "icon set: nerd, emoji or none") lang = flag.String("lang", "", "display language: en or pl") + pollen = flag.String("pollen", "", "pollen species to show: a list, or all / none") noGraph = flag.Bool("no-graph", false, "table only, no chart") weather = flag.Bool("weather", false, "forecast only: no sun times, summary, pollen or chart") noWarn = flag.Bool("no-warnings", false, "omit IMGW warnings") @@ -41,6 +43,7 @@ func run() int { noColor = flag.Bool("no-color", false, "plain output") pick = flag.Int("pick", 0, "choose the Nth place when the name is ambiguous") showCfg = flag.Bool("config", false, "print the config file path and exit") + showVer = flag.Bool("version", false, "print the version and exit") ) flag.Usage = usage // Go's flag package stops at the first non-flag argument, so @@ -59,6 +62,11 @@ func run() int { rest = flag.Args()[1:] } + if *showVer { + fmt.Printf("prognosis %s\n", version) + return 0 + } + cfgPath := config.Path() if *showCfg { fmt.Println(cfgPath) @@ -87,6 +95,16 @@ func run() int { if *lang != "" { cfg.DisplayLang = *lang } + if *pollen != "" { + switch *pollen { + case "all": + cfg.Pollen, cfg.PollenExplicit = append([]string(nil), config.AllSpecies...), false + case "none": + cfg.Pollen, cfg.PollenExplicit = nil, false + default: + cfg.Pollen, cfg.PollenExplicit = splitList(*pollen), true + } + } if *noGraph { cfg.Graph = false } @@ -147,10 +165,9 @@ func run() int { cfg.Location = place } if cfg.Location == "" { - cfg.Location = locationFromWegorc() - } - if cfg.Location == "" { - fmt.Fprintf(os.Stderr, "prognosis: no location set in %s and none in ~/%s\n", cfgPath, wegorc) + fmt.Fprintf(os.Stderr, + "prognosis: no location set. Put one in %s:\n\n location=Krakow\n\n"+ + "or pass it for one run: prognosis -l Krakow\n", cfgPath) return 2 } @@ -180,6 +197,17 @@ func run() int { fmt.Fprintf(os.Stderr, "note: %d %s %d\n", len(data.Rows), cat.Word("hours_available"), cfg.Hours) } + // Custom air-quality columns need a second request, made only when the + // config actually declares one. + if fields := cfg.AirFields(); len(fields) > 0 { + hourly, err := openmeteo.AirHourly(geo.Lat, geo.Lon, cfg.Hours, fields) + if err != nil { + fmt.Fprintf(os.Stderr, "note: air-quality data unavailable: %v\n", err) + } else { + mergeCustom(data.Rows, hourly, cfg) + } + } + view := render.View{ Label: geo.Label, TZ: data.TZ, Rows: data.Rows, Sun: data.Sun, Daily: data.Daily, @@ -350,33 +378,6 @@ func parseCoords(s string) (float64, float64, bool) { return a, b, true } -// locationFromWegorc reads location= from wego's config, so the two tools never -// disagree about where you are. -func locationFromWegorc() string { - home, err := os.UserHomeDir() - if err != nil { - return "" - } - f, err := os.Open(home + "/" + wegorc) - if err != nil { - return "" - } - defer f.Close() - sc := bufio.NewScanner(f) - for sc.Scan() { - line := strings.TrimSpace(sc.Text()) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - if k, v, ok := strings.Cut(line, "="); ok && strings.TrimSpace(k) == "location" { - if v = strings.TrimSpace(v); v != "" { - return v - } - } - } - return "" -} - func shouldColour(mode string) bool { switch mode { case "never": @@ -424,18 +425,41 @@ func usage() { usage: prognosis [flags] [place] flags: - -l PLACE place to query (default: config, then ~/.wegorc) + -l PLACE place to query (default: location= in the config file) -pick N choose the Nth place when the name matches several -n N hours ahead to show -d N days ahead to show, 24h each (max %d) -columns LIST comma-separated columns; valid: %s -icons SET nerd, emoji or none -lang LANG en or pl + -pollen LIST pollen species to show: a list, or all / none -no-graph table only -weather forecast only: no sun times, summary, pollen or chart -no-warnings omit IMGW warnings -ascii ASCII only, so an SMS stays in GSM-7 (160 chars, not 70) -no-color plain output -config print the config file path and exit + -version print the version and exit `, openmeteo.MaxForecastDays-1, strings.Join(config.ValidColumns(), ", ")) } + +// mergeCustom copies air-quality values onto the matching forecast hour. +// +// Custom values are stored under the column's own name, prefixed, so they can +// never collide with an API field name already in the row. +func mergeCustom(rows []openmeteo.Row, hourly map[string]map[string]float64, cfg config.Config) { + for i, r := range rows { + byField, ok := hourly[r.When.Format("2006-01-02T15:04")] + if !ok { + continue + } + for name, cc := range cfg.Custom { + if cc.Source != "air" { + continue + } + if v, ok := byField[cc.Field]; ok { + rows[i].Vals[render.CustomKey(name)] = v + } + } + } +} |
