diff --git a/cmd/forecast.go b/cmd/forecast.go new file mode 100644 index 0000000..57d57bf --- /dev/null +++ b/cmd/forecast.go @@ -0,0 +1,100 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "git.sr.ht/~timharek/yr-go/cmd/flags" + "git.sr.ht/~timharek/yr-go/internal/nominatim" + "git.sr.ht/~timharek/yr-go/yr" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" + "github.com/spf13/cobra" +) + +var forecastCmd = &cobra.Command{ + Use: "forecast ", + Aliases: []string{"f", "ls"}, + Short: "Get forecasted weather", + Args: cobra.MaximumNArgs(1), + Run: forecast, +} + +func init() { + rootCmd.AddCommand(forecastCmd) +} + +func forecast(cmd *cobra.Command, args []string) { + isJson, err := cmd.Flags().GetBool(flags.JSON) + cobra.CheckErr(err) + lon, _ := cmd.Flags().GetFloat64(flags.LON) + lat, _ := cmd.Flags().GetFloat64(flags.LAT) + + c, err := yr.New() + cobra.CheckErr(err) + + if len(args) == 0 && (lon == 0 || lat == 0) { + fmt.Fprintln(os.Stderr, "No location or coordinates provided.") + os.Exit(1) + } + var f []yr.Forecast + if len(args) == 0 { + f, err = c.ForecastCoords(&nominatim.Coordinates{Longitude: lon, Latitude: lat}, nil) + cobra.CheckErr(err) + + } else { + location := args[0] + f, err = c.Forecast(location) + cobra.CheckErr(err) + + } + + if isJson { + j, err := json.MarshalIndent(f, "", " ") + cobra.CheckErr(err) + fmt.Printf("%s", j) + return + } + + re := lipgloss.NewRenderer(os.Stdout) + + const ( + white = lipgloss.Color("#fff") + lightGray = lipgloss.Color("#dedede") + ) + var ( + HeaderStyle = re.NewStyle().Foreground(white).Bold(true).Align(lipgloss.Center) + CellStyle = re.NewStyle().Padding(0, 2) + EvenRowStyle = CellStyle.Foreground(lightGray) + BorderStyle = lipgloss.NewStyle().Foreground(white) + ) + + t := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(BorderStyle). + StyleFunc(func(row, col int) lipgloss.Style { + switch { + case row == 0: + return HeaderStyle + case row%2 == 0: + return EvenRowStyle + default: + return CellStyle + } + }). + Headers("time", "temp.", "rain", "wind") + + for _, item := range f { + t.Row( + item.Time.Format(time.DateTime), + fmt.Sprintf("%.1f °C", item.Temperature), + fmt.Sprintf("%.1f mm", item.Percipitation), + fmt.Sprintf("%.1f m/s %s", item.Wind.Speed, item.Wind.DirectionToString()), + ) + + } + + fmt.Println(t) +} diff --git a/go.mod b/go.mod index c910b15..936eb6b 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,21 @@ require ( ) require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/lipgloss v0.13.0 // indirect + github.com/charmbracelet/x/ansi v0.1.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.19.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6a51f40..6c95247 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= +github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -11,9 +17,20 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -24,6 +41,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/yr/yr.go b/yr/yr.go index 00b2730..92ee4b7 100644 --- a/yr/yr.go +++ b/yr/yr.go @@ -4,6 +4,7 @@ import ( "cmp" "fmt" "slices" + "time" "git.sr.ht/~timharek/yr-go/internal/met" "git.sr.ht/~timharek/yr-go/internal/nominatim" @@ -66,9 +67,10 @@ func windDirection(d float64) string { type Forecast struct { nominatim.LookupResult - Temperature float64 `json:"temperature"` // In celsius - Percipitation float64 `json:"percipitation"` // In mm - Wind wind `json:"wind"` // In m/s + Time time.Time `json:"time"` + Temperature float64 `json:"temperature"` // In celsius + Percipitation float64 `json:"percipitation"` // In mm + Wind wind `json:"wind"` // In m/s } func (c *Client) Now(q string) (*Forecast, error) { @@ -155,6 +157,7 @@ func (c *Client) ForecastCoords(coords *nominatim.Coordinates, location *string) forecasts = append(forecasts, Forecast{ LookupResult: *lookupResult, + Time: t.Time, Temperature: t.Data.Instant.Details.AirTemperature, Percipitation: float64(percipitation), Wind: wind{