discovery: fix ireturn on the shared cache reader · Entire

discovery: fix ireturn on the shared cache reader

e67d7e1→main·

toothbrush·1mo ago·2 files·+18 added/-13 removed

CI's golangci-lint flagged ireturn on the generic readCacheFile, which returned its type parameter T. (Locally the nolint directive that silenced it kept getting stripped by golangci-lint run --fix's nolintlint autofix, so it only surfaced in CI.)

Replace it with loadCacheFile(path, *T, newEmpty) error: it fills a destination pointer and returns error, which ireturn allows, while still sharing the read/unmarshal/corrupt-reset logic across both caches.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

6d6f3baefcd9View transcript

Changes

2

70 unmodified lines

71
72
73
74
74
75
76
77
78
79
49 unmodified lines

129
130
131
130
131
132
133
132
133
134
135
136
137
138
139
140
136
137
141
142
139
143
141
144
145
143
144
146
147
148
146
149
150
151
152

70 unmodified lines

}

func readCacheNoLock(path string) (ClusterCache, error) {
    return readCacheFile(path, func() ClusterCache { return make(ClusterCache) })
    cache := make(ClusterCache)
    err := loadCacheFile(path, &cache, func() ClusterCache { return make(ClusterCache) })
    return cache, err
}

func writeCacheNoLock(path string, cache ClusterCache) error {
    49 unmodified lines
    return func() { _ = fl.Unlock() }, nil //nolint:errcheck // unlock failure is non-fatal
}

// readCacheFile reads and unmarshals a JSON cache file. A missing file or a
// corrupt one both yield a fresh empty value (newEmpty), so a damaged cache
// self-heals on the next write instead of wedging callers.
func readCacheFile[T any](path string, newEmpty func() T) (T, error) {
// loadCacheFile reads path and unmarshals it into dst. A missing file leaves
// dst at its caller-initialized (empty) value; a corrupt file resets dst via
// newEmpty so a damaged cache self-heals on the next write instead of wedging
// callers. Returns an error only on a genuine read failure. Returning error
// (rather than the cache value itself) keeps this generic helper off the
// ireturn linter while still sharing the read/unmarshal logic across caches.
func loadCacheFile[T any](path string, dst *T, newEmpty func() T) error {
    data, exists, err := readCacheBytes(path)
    if err != nil {
        var zero T
        return zero, err
    }
    c := newEmpty()
    if !exists {
        return c, nil
    }
    if err := json.Unmarshal(data, &c); err != nil {
        return newEmpty(), nil //nolint:nilerr // intentional: treat corrupt cache as empty
    }
    if json.Unmarshal(data, dst) != nil {
        *dst = newEmpty() // corrupt → start fresh
    }
    return c, nil
}
// writeCacheFile marshals v and writes it atomically (tmp + rename).

Minternal/entireclient/discovery/cache.go+15/-12

48 unmodified lines

}

func readClusterCoresNoLock(path string) (ClusterCoresCache, error) { return readCacheFile(path, func() ClusterCoresCache { return make(ClusterCoresCache) }) cache := make(ClusterCoresCache) err := loadCacheFile(path, &cache, func() ClusterCoresCache { return make(ClusterCoresCache) }) return cache, err }

func writeClusterCoresNoLock(path string, cache ClusterCoresCache) error {