diff --git a/internal/imgcache/eviction.go b/internal/imgcache/eviction.go index 0deb579..8bf6edb 100644 --- a/internal/imgcache/eviction.go +++ b/internal/imgcache/eviction.go @@ -2,6 +2,7 @@ package imgcache import ( "context" + "database/sql" "encoding/json" "fmt" "io/fs" @@ -647,32 +648,43 @@ func (c *Cache) reconcileVariantRows(ctx context.Context) error { // allVariantKeys returns every tracked variant cache key. func (c *Cache) allVariantKeys(ctx context.Context) ([]VariantKey, error) { - rows, err := c.db.QueryContext(ctx, `SELECT cache_key FROM variant_content`) + return queryStringColumn[VariantKey](ctx, c.db, + `SELECT cache_key FROM variant_content`, "variant keys", "variant key") +} + +// queryStringColumn runs a single-column query and returns the column +// values as T. plural names the set for the query and scan failure +// messages; singular names one row for the scan and iteration failure +// messages. +func queryStringColumn[T ~string]( + ctx context.Context, db *sql.DB, query, plural, singular string, +) ([]T, error) { + rows, err := db.QueryContext(ctx, query) if err != nil { - return nil, fmt.Errorf("failed to query variant keys: %w", err) + return nil, fmt.Errorf("failed to query %s: %w", plural, err) } defer func() { _ = rows.Close() }() - var keys []VariantKey + var values []T for rows.Next() { - var key string + var value string - err := rows.Scan(&key) + err := rows.Scan(&value) if err != nil { - return nil, fmt.Errorf("failed to scan variant key: %w", err) + return nil, fmt.Errorf("failed to scan %s: %w", singular, err) } - keys = append(keys, VariantKey(key)) + values = append(values, T(value)) } err = rows.Err() if err != nil { - return nil, fmt.Errorf("variant key iteration failed: %w", err) + return nil, fmt.Errorf("%s iteration failed: %w", singular, err) } - return keys, nil + return values, nil } // reconcileSourceFiles walks the source content directory, removing @@ -763,32 +775,9 @@ func (c *Cache) reconcileSourceRows(ctx context.Context) error { // allSourceContentHashes returns every tracked source content hash. func (c *Cache) allSourceContentHashes(ctx context.Context) ([]ContentHash, error) { - rows, err := c.db.QueryContext(ctx, `SELECT content_hash FROM source_content`) - if err != nil { - return nil, fmt.Errorf("failed to query source content hashes: %w", err) - } - - defer func() { _ = rows.Close() }() - - var hashes []ContentHash - - for rows.Next() { - var hash string - - err := rows.Scan(&hash) - if err != nil { - return nil, fmt.Errorf("failed to scan content hash: %w", err) - } - - hashes = append(hashes, ContentHash(hash)) - } - - err = rows.Err() - if err != nil { - return nil, fmt.Errorf("content hash iteration failed: %w", err) - } - - return hashes, nil + return queryStringColumn[ContentHash](ctx, c.db, + `SELECT content_hash FROM source_content`, + "source content hashes", "content hash") } // sweepStaleTempFile removes a temp file left behind by a crashed