fix: use x/etag (#122)

Carlos Alexandro Becker created

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

Change summary

go.mod                |  7 +++++--
go.sum                |  2 ++
internal/etag/etag.go | 13 -------------
main.go               |  9 ++++-----
pkg/catwalk/client.go | 10 +++-------
5 files changed, 14 insertions(+), 27 deletions(-)

Detailed changes

go.mod 🔗

@@ -1,8 +1,11 @@
 module github.com/charmbracelet/catwalk
 
-go 1.24.3
+go 1.25.5
 
-require github.com/prometheus/client_golang v1.23.2
+require (
+	github.com/charmbracelet/x/etag v0.2.0
+	github.com/prometheus/client_golang v1.23.2
+)
 
 require (
 	github.com/beorn7/perks v1.0.1 // indirect

go.sum 🔗

@@ -2,6 +2,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/charmbracelet/x/etag v0.2.0 h1:Euj1VkheoHfTYA9y+TCwkeXF/hN8Fb9l4LqZl79pt04=
+github.com/charmbracelet/x/etag v0.2.0/go.mod h1:C1B7/bsgvzzxpfu0Rabbd+rTHJa5TmC/qgTseCf6DF0=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

internal/etag/etag.go 🔗

@@ -1,13 +0,0 @@
-// Package etag can create the etag value for the given data.
-package etag
-
-import (
-	"crypto/sha256"
-	"fmt"
-)
-
-// Of returns the etag for the given data.
-func Of(data []byte) string {
-	hash := sha256.Sum256(data)
-	return fmt.Sprintf(`%x`, hash[:16])
-}

main.go 🔗

@@ -4,14 +4,13 @@ package main
 
 import (
 	"encoding/json"
-	"fmt"
 	"log"
 	"net/http"
 	"time"
 
 	"github.com/charmbracelet/catwalk/internal/deprecated"
-	"github.com/charmbracelet/catwalk/internal/etag"
 	"github.com/charmbracelet/catwalk/internal/providers"
+	"github.com/charmbracelet/x/etag"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -35,12 +34,12 @@ func init() {
 	if err != nil {
 		log.Fatal("Failed to marshal providers:", err)
 	}
-	providersETag = fmt.Sprintf(`"%s"`, etag.Of(providersJSON))
+	providersETag = etag.Of(providersJSON)
 }
 
 func providersHandler(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("ETag", providersETag)
+	etag.Response(w, providersETag)
 
 	if r.Method == http.MethodHead {
 		return
@@ -53,7 +52,7 @@ func providersHandler(w http.ResponseWriter, r *http.Request) {
 
 	counter.Inc()
 
-	if match := r.Header.Get("If-None-Match"); match == providersETag {
+	if etag.Matches(r, providersETag) {
 		w.WriteHeader(http.StatusNotModified)
 		return
 	}

pkg/catwalk/client.go 🔗

@@ -8,7 +8,7 @@ import (
 	"net/http"
 	"os"
 
-	"github.com/charmbracelet/catwalk/internal/etag"
+	xetag "github.com/charmbracelet/x/etag"
 )
 
 const defaultURL = "http://localhost:8080"
@@ -41,7 +41,7 @@ func NewWithURL(url string) *Client {
 var ErrNotModified = fmt.Errorf("not modified")
 
 // Etag returns the ETag for the given data.
-func Etag(data []byte) string { return etag.Of(data) }
+func Etag(data []byte) string { return xetag.Of(data) }
 
 // GetProviders retrieves all available providers from the service.
 func (c *Client) GetProviders(ctx context.Context, etag string) ([]Provider, error) {
@@ -54,11 +54,7 @@ func (c *Client) GetProviders(ctx context.Context, etag string) ([]Provider, err
 	if err != nil {
 		return nil, fmt.Errorf("could not create request: %w", err)
 	}
-
-	if etag != "" {
-		// It needs to be quoted:
-		req.Header.Add("If-None-Match", fmt.Sprintf(`"%s"`, etag))
-	}
+	xetag.Request(req, etag)
 
 	resp, err := c.httpClient.Do(req)
 	if err != nil {