refactor: extract queryStringColumn helper for the accounting queries (dupl)
All checks were successful
check / check (push) Successful in 2m23s

allVariantKeys and allSourceContentHashes were byte-for-byte the same
query-scan-collect loop over a single string column, differing only in
the SQL, the result type and the noun in their error messages. Both now
delegate to a generic queryStringColumn helper.

Extracted rather than suppressed with a nolint: the duplication was
real, and the two call sites are the only places this shape appears.
Every error message is preserved verbatim, which is why the helper takes
both a plural and a singular noun.
This commit is contained in:
2026-08-09 13:43:09 +00:00
parent 90d5c1f73a
commit 3fa3c4ed19

View File

@@ -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