Compare commits

..

No commits in common. "ffe81b005dbd9e786e3e899d23897657337c9129" and "dbcaca54c7a43b3ee245c433d020d80e19ae3b72" have entirely different histories.

12 changed files with 41 additions and 81 deletions

View file

@ -22,7 +22,9 @@ tasks:
complete-build complete-build
fi fi
make GOOS="darwin" ARCH="amd64" TAG=$tag build-release version=$(echo "$tag" | tr -d 'v')
make GOOS="darwin" ARCH="aarch64" TAG=$tag build-release
make GOOS="linux" ARCH="amd64" TAG=$tag build-release make GOOS="darwin" ARCH="amd64" build-release
make GOOS="windows" ARCH="amd64" TAG=$tag build-release make GOOS="dawrin" ARCH="aarch64" build-release
make GOOS="linux" ARCH="amd64" build-release
make GOOS="windows" ARCH="amd64" build-release

View file

@ -8,11 +8,11 @@ endif
bindir = $(prefix)/bin bindir = $(prefix)/bin
builddir = ./build builddir = ./build
VERSION = 0.0.6 VERSION = 0.0.5
.PHONY: build .PHONY: build
build: build:
go build -o $(builddir)/ go build -o $(builddir)
.PHONY: install .PHONY: install
install: build install: build
@ -30,7 +30,7 @@ build-release: build
cp $(builddir)/yr* yr-$(VERSION)/ cp $(builddir)/yr* yr-$(VERSION)/
tar czf yr-$(VERSION)-$(GOOS)-$(ARCH).tar.gz yr-$(VERSION) tar czf yr-$(VERSION)-$(GOOS)-$(ARCH).tar.gz yr-$(VERSION)
hut git artifact upload --rev $(TAG) yr-$(VERSION)-$(GOOS)-$(ARCH).tar.gz hut git artifact upload --rev $(tag) yr-$(VERSION)-$(GOOS)-$(ARCH).tar.gz
.PHONY: test .PHONY: test
test: test:

View file

@ -21,7 +21,7 @@ var (
func init() { func init() {
rootCmd.AddCommand(forecastCmd) rootCmd.AddCommand(forecastCmd)
forecastCmd.Flags().IntVarP(&interval, "interval", "i", 0, "Number of hours to output") rootCmd.Flags().IntVarP(&interval, "interval", "i", 0, "Number of hours to output")
} }
func forecast(cmd *cobra.Command, args []string) { func forecast(cmd *cobra.Command, args []string) {
@ -39,13 +39,12 @@ func forecast(cmd *cobra.Command, args []string) {
outputJson(f.Forecast) outputJson(f.Forecast)
} }
t := table.New("time", "temp.", "rain", "wind") t := table.New()
for _, item := range f.Forecast { for _, item := range f.Forecast {
if !isUTC { if !isUTC {
item.Time = item.Time.Local() item.Time = item.Time.Local()
} }
t.Row( t.Row(
item.Time.Format("Mon, 2 Jan 15:04"), item.Time.Format("Mon, 2 Jan 15:04"),
fmt.Sprintf("%.1f °C", item.Temperature), fmt.Sprintf("%.1f °C", item.Temperature),

View file

@ -7,7 +7,8 @@ import (
"github.com/charmbracelet/lipgloss/table" "github.com/charmbracelet/lipgloss/table"
) )
func New(headers ...string) *table.Table { // TODO: Make more generic
func New() *table.Table {
re := lipgloss.NewRenderer(os.Stdout) re := lipgloss.NewRenderer(os.Stdout)
const ( const (
@ -15,41 +16,23 @@ func New(headers ...string) *table.Table {
lightGray = lipgloss.Color("#dedede") lightGray = lipgloss.Color("#dedede")
) )
var ( var (
HeaderStyle = re. HeaderStyle = re.NewStyle().Foreground(white).Bold(true).Align(lipgloss.Center)
NewStyle(). CellStyle = re.NewStyle().Padding(0, 2)
Foreground(white).
Bold(true).
Align(lipgloss.Center)
CellStyle = re.NewStyle().
Padding(0, 2).
AlignHorizontal(lipgloss.Right)
EvenRowStyle = CellStyle.Foreground(lightGray) EvenRowStyle = CellStyle.Foreground(lightGray)
) )
t := table.New(). t := table.New().
Border(lipgloss.RoundedBorder()). Border(lipgloss.RoundedBorder()).
StyleFunc(func(row, col int) lipgloss.Style { StyleFunc(func(row, col int) lipgloss.Style {
if row == 0 {
return HeaderStyle
}
var style lipgloss.Style
switch { switch {
case row == 0:
return HeaderStyle
case row%2 == 0: case row%2 == 0:
style = EvenRowStyle return EvenRowStyle
default: default:
style = CellStyle return CellStyle
} }
// right align numeric values for better readability
if col > 0 && row > 1 {
style.AlignHorizontal(lipgloss.Right)
}
return style
}). }).
Headers(headers...) Headers("time", "temp.", "rain", "wind")
return t return t
} }

View file

@ -9,7 +9,7 @@ import (
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "yr", Use: "yr",
Short: "yr is CLI tool for weather forecasts", Short: "yr is CLI tool for weather forecasts",
Version: "v0.0.6", Version: "v0.0.5",
} }
func Execute() { func Execute() {

View file

@ -30,7 +30,7 @@ func today(cmd *cobra.Command, args []string) {
outputJson(f.Forecast) outputJson(f.Forecast)
} }
t := table.New("time", "temp.", "rain", "wind") t := table.New()
today := time.Now() today := time.Now()
for _, item := range f.Forecast { for _, item := range f.Forecast {

View file

@ -36,7 +36,7 @@ func tomorrow(cmd *cobra.Command, args []string) {
os.Exit(0) os.Exit(0)
} }
t := table.New("time", "temp.", "rain", "wind") t := table.New()
tomorrows := []yr.Forecast{} tomorrows := []yr.Forecast{}

28
pkg/cache/cache.go vendored
View file

@ -23,32 +23,22 @@ func New[T any](path string) *Cache[T] {
return &Cache[T]{path} return &Cache[T]{path}
} }
var (
ErrorUnableToMarshalJSON = errors.New("unable to marshal as JSON")
ErrorUnableToEnsureCacheDir = errors.New("unable to ensure cache-dir is present")
ErrorUnableToWriteCache = errors.New("unable to write data to cache file")
ErrorUnableToOpenCacheFile = errors.New("unable to open cache")
ErrorUnableToReadCacheFile = errors.New("unable to read cache")
ErrorUnableToUnmarshalCacheFile = errors.New("unable to unmarshal JSON from cache-file")
ErrorUnableToReadCachePath = errors.New("unable to read from cache path")
)
// Add data as JSON-file into cache-dir, returns nil if success // Add data as JSON-file into cache-dir, returns nil if success
func (c *Cache[T]) Add(name string, data any) error { func (c *Cache[T]) Add(name string, data any) error {
j, err := json.MarshalIndent(data, "", " ") j, err := json.MarshalIndent(data, "", " ")
if err != nil { if err != nil {
return ErrorUnableToMarshalJSON return fmt.Errorf("Failed to convert data as JSON.")
} }
err = os.MkdirAll(c.Path, 0646) err = os.MkdirAll(c.Path, 0646)
if err != nil { if err != nil {
return ErrorUnableToEnsureCacheDir return fmt.Errorf("Failed to ensure cache-dir is present.")
} }
filePath := filepath.Join(c.Path, filename(name)) filePath := filepath.Join(c.Path, filename(name))
d := []byte(j) d := []byte(j)
err = os.WriteFile(filePath, d, 0646) err = os.WriteFile(filePath, d, 0646)
if err != nil { if err != nil {
return ErrorUnableToWriteCache return fmt.Errorf("Failed write data to cache-file.")
} }
return nil return nil
@ -63,19 +53,19 @@ func (c *Cache[T]) Get(name string) (*T, error) {
rawFile, err := os.Open(filePath) rawFile, err := os.Open(filePath)
if err != nil { if err != nil {
return nil, ErrorUnableToOpenCacheFile return nil, fmt.Errorf("Failed to read cache-file.")
} }
var result T var result T
bytes, err := io.ReadAll(rawFile) bytes, err := io.ReadAll(rawFile)
if err != nil { if err != nil {
return nil, ErrorUnableToReadCacheFile return nil, fmt.Errorf("Failed to read data from cache-file.")
} }
err = json.Unmarshal(bytes, &result) err = json.Unmarshal(bytes, &result)
if err != nil { if err != nil {
return nil, ErrorUnableToUnmarshalCacheFile return nil, fmt.Errorf("Failed to parse JSON from cache-file.")
} }
return &result, nil return &result, nil
@ -90,7 +80,7 @@ func (c *Cache[T]) List() ([]Saved[T], error) {
var result []Saved[T] var result []Saved[T]
filenames, err := list(c.Path) filenames, err := list(c.Path)
if err != nil { if err != nil {
return nil, ErrorUnableToReadCachePath return nil, fmt.Errorf("Unable to read from cache path")
} }
if len(filenames) == 0 { if len(filenames) == 0 {
@ -112,7 +102,7 @@ func (c *Cache[T]) List() ([]Saved[T], error) {
func list(path string) ([]string, error) { func list(path string) ([]string, error) {
dir, err := os.ReadDir(path) dir, err := os.ReadDir(path)
if err != nil { if err != nil {
return nil, ErrorUnableToReadCachePath return nil, fmt.Errorf("Unable to read from path")
} }
var filenames []string var filenames []string
@ -155,5 +145,5 @@ func IsExpired(expire time.Time, now *time.Time) bool {
now = &tmp now = &tmp
} }
return now.After(expire) return now.Unix() >= expire.Unix()
} }

View file

@ -55,7 +55,7 @@ func TestIsExpired(t *testing.T) {
assert.NoError(err) assert.NoError(err)
result = IsExpired(expirationTime, &now) result = IsExpired(expirationTime, &now)
assert.False(result) assert.True(result)
now, err = time.Parse(layout, "2024-10-04 22:23:00") now, err = time.Parse(layout, "2024-10-04 22:23:00")
assert.NoError(err) assert.NoError(err)

View file

@ -3,7 +3,6 @@ package met
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -14,18 +13,10 @@ type Met struct {
siteName string siteName string
} }
var (
ErrorMissingSiteName = errors.New("siteName must be defined")
ErrorMissingHeaderExpires = errors.New("missing header 'Expires'")
ErrorMissingHeaderLastModified = errors.New("missing header 'Last-Modified'")
ErrorParseExpires = errors.New("unable to parse 'Expires'")
ErrorParseLastModified = errors.New("unable to parse 'Last-Modified'")
)
// Returns valid Met client or error if empty siteName is provided // Returns valid Met client or error if empty siteName is provided
func New(siteName string) (*Met, error) { func New(siteName string) (*Met, error) {
if siteName == "" { if siteName == "" {
return nil, ErrorMissingSiteName return nil, fmt.Errorf("`siteName` must be defined.")
} }
return &Met{ return &Met{
@ -69,19 +60,19 @@ func (m *Met) Forecast(lat, lon float64, alt *int) (*LocationForecastResult, err
expiresRaw, ok := resp.Header["Expires"] expiresRaw, ok := resp.Header["Expires"]
if !ok { if !ok {
return nil, ErrorMissingHeaderExpires return nil, fmt.Errorf("Unable to get 'Expires' header")
} }
expires, err := time.Parse(time.RFC1123, expiresRaw[0]) expires, err := time.Parse(time.RFC1123, expiresRaw[0])
if err != nil { if err != nil {
return nil, errors.Join(ErrorParseExpires, err) return nil, fmt.Errorf("Unable to parse `expiresRaw`: %w", err)
} }
lastModifiedRaw, ok := resp.Header["Last-Modified"] lastModifiedRaw, ok := resp.Header["Last-Modified"]
if !ok { if !ok {
return nil, ErrorMissingHeaderLastModified return nil, fmt.Errorf("Unable to get 'Last-Modified' header")
} }
lastModified, err := time.Parse(time.RFC1123, lastModifiedRaw[0]) lastModified, err := time.Parse(time.RFC1123, lastModifiedRaw[0])
if err != nil { if err != nil {
return nil, errors.Join(ErrorParseLastModified, err) return nil, fmt.Errorf("Unable to parse `Last-Modified`: %w", err)
} }
forecast := LocationForecast{} forecast := LocationForecast{}

View file

@ -2,7 +2,6 @@ package nominatim
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -15,13 +14,9 @@ type Nominatim struct {
siteName string siteName string
} }
var (
ErrorMissingSiteName = errors.New("siteName must be defined")
)
func New(siteName string) (*Nominatim, error) { func New(siteName string) (*Nominatim, error) {
if siteName == "" { if siteName == "" {
return nil, ErrorMissingSiteName return nil, fmt.Errorf("`siteName` must be defined.")
} }
return &Nominatim{ return &Nominatim{
@ -99,7 +94,7 @@ func (n *Nominatim) Reverse(lat, lon float64) (*ReverseResult, error) {
} }
if allFieldsNil(result) { if allFieldsNil(result) {
return nil, fmt.Errorf("no result") return nil, fmt.Errorf("No result")
} }
return &result, nil return &result, nil

View file

@ -165,7 +165,7 @@ func (c *Client) ForecastCoords(coords *nominatim.Coordinates, location *string)
} }
if location == nil { if location == nil {
return nil, fmt.Errorf("location name is not set") return nil, fmt.Errorf("Location name is not set")
} }
ts := f.Properties.Timeseries ts := f.Properties.Timeseries