weather/main.go

153 lines
3.5 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"os"
"strings"
"time"
)
const (
defaultForecastTTL = 10 * time.Minute
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
}
func run() error {
var (
search string
includeDesc bool
prettyOutput bool
temperatureU string
clearCache bool
refresh bool
iconTest bool
radarMode bool
)
flag.StringVar(&search, "s", "", "Search location (e.g. \"Somerville, MA\")")
flag.StringVar(&search, "search", "", "Search location (e.g. \"Somerville, MA\")")
flag.BoolVar(&includeDesc, "d", false, "Include forecast description text")
flag.BoolVar(&includeDesc, "desc", false, "Include forecast description text")
flag.BoolVar(&prettyOutput, "pretty", false, "Enable NerdFont icons")
flag.BoolVar(&prettyOutput, "p", false, "Enable NerdFont icons")
flag.StringVar(&temperatureU, "u", "F", "Temperature unit (F or C)")
flag.StringVar(&temperatureU, "units", "F", "Temperature unit (F or C)")
flag.BoolVar(&clearCache, "clear-cache", false, "Clear cached location and forecast data")
flag.BoolVar(&clearCache, "c", false, "Clear cached location and forecast data")
flag.BoolVar(&refresh, "refresh", false, "Force refresh forecast data")
flag.BoolVar(&refresh, "r", false, "Force refresh forecast data")
flag.BoolVar(&iconTest, "icon-test", false, "Print all icons used by the program")
flag.BoolVar(&iconTest, "i", false, "Print all icons used by the program")
flag.BoolVar(&radarMode, "radar", false, "Display radar loop for current location via mpv")
flag.BoolVar(&radarMode, "R", false, "Display radar loop for current location via mpv")
flag.Parse()
ua, err := buildUserAgent()
if err != nil {
return err
}
temperatureU = strings.ToUpper(strings.TrimSpace(temperatureU))
if temperatureU == "" {
temperatureU = "F"
}
if temperatureU != "F" && temperatureU != "C" {
return fmt.Errorf("invalid unit: %s", temperatureU)
}
cacheDir, err := cacheRoot()
if err != nil {
return err
}
cache := NewCache(cacheDir)
ctx := context.Background()
if iconTest {
printIconTest()
return nil
}
if radarMode {
if err := requireMPV(); err != nil {
return err
}
}
if clearCache {
if err := cache.Clear(); err != nil {
return err
}
return nil
}
var loc Location
if search != "" {
results, err := SearchLocations(ctx, search, ua)
if err != nil {
return err
}
loc, err = SelectLocation(results)
if err != nil {
return err
}
if err := cache.SaveLocation(search, loc); err != nil {
return err
}
} else {
var ok bool
loc, ok, err = cache.LoadLocation()
if err != nil {
return err
}
if !ok {
return fmt.Errorf("no cached location found; use -s to set a location")
}
}
if radarMode {
return runRadar(ctx, cache, loc, search, ua)
}
client := NewNWSClient(ua)
var period ForecastPeriod
if refresh {
var err error
period, err = client.FetchForecast(ctx, loc, temperatureU)
if err != nil {
return err
}
if err := cache.SaveForecast(loc, temperatureU, period); err != nil {
return err
}
} else {
var err error
var fromCache bool
period, fromCache, err = cache.LoadForecast(loc, temperatureU, defaultForecastTTL)
if err != nil {
return err
}
if !fromCache {
period, err = client.FetchForecast(ctx, loc, temperatureU)
if err != nil {
return err
}
if err := cache.SaveForecast(loc, temperatureU, period); err != nil {
return err
}
}
}
line := FormatOutput(loc, period, includeDesc, prettyOutput)
fmt.Println(line)
return nil
}