Introduce structured error types and bump Go/linter versions

Replace ad-hoc fmt.Errorf/errors.New calls with typed error structs across config, sarin, and script packages to enable type-based error handling. Add script-specific error handlers in CLI entry point. Fix variable shadowing bug in Worker for scriptTransformer. Bump Go to 1.25.7 and golangci-lint to v2.8.0.
This commit is contained in:
2026-02-08 02:54:54 +04:00
parent e83eacf380
commit 6dafc082ed
20 changed files with 473 additions and 106 deletions

View File

@@ -1,7 +1,6 @@
package sarin
import (
"fmt"
"io"
"net/http"
"os"
@@ -10,6 +9,8 @@ import (
"strings"
"sync"
"time"
"go.aykhans.me/sarin/internal/types"
)
// CachedFile holds the cached content and metadata of a file.
@@ -31,6 +32,10 @@ func NewFileCache(requestTimeout time.Duration) *FileCache {
// GetOrLoad retrieves a file from cache or loads it using the provided source.
// The source can be a local file path or an HTTP/HTTPS URL.
// It can return the following errors:
// - types.FileReadError
// - types.HTTPFetchError
// - types.HTTPStatusError
func (fc *FileCache) GetOrLoad(source string) (*CachedFile, error) {
if val, ok := fc.cache.Load(source); ok {
return val.(*CachedFile), nil
@@ -59,14 +64,21 @@ func (fc *FileCache) GetOrLoad(source string) (*CachedFile, error) {
return actual.(*CachedFile), nil
}
// readLocalFile reads a file from the local filesystem and returns its content and filename.
// It can return the following errors:
// - types.FileReadError
func (fc *FileCache) readLocalFile(filePath string) ([]byte, string, error) {
content, err := os.ReadFile(filePath) //nolint:gosec
if err != nil {
return nil, "", fmt.Errorf("failed to read file %s: %w", filePath, err)
return nil, "", types.NewFileReadError(filePath, err)
}
return content, filepath.Base(filePath), nil
}
// fetchURL downloads file contents from an HTTP/HTTPS URL.
// It can return the following errors:
// - types.HTTPFetchError
// - types.HTTPStatusError
func (fc *FileCache) fetchURL(url string) ([]byte, string, error) {
client := &http.Client{
Timeout: fc.requestTimeout,
@@ -74,17 +86,17 @@ func (fc *FileCache) fetchURL(url string) ([]byte, string, error) {
resp, err := client.Get(url)
if err != nil {
return nil, "", fmt.Errorf("failed to fetch URL %s: %w", url, err)
return nil, "", types.NewHTTPFetchError(url, err)
}
defer resp.Body.Close() //nolint:errcheck
if resp.StatusCode != http.StatusOK {
return nil, "", fmt.Errorf("failed to fetch URL %s: HTTP %d", url, resp.StatusCode)
return nil, "", types.NewHTTPStatusError(url, resp.StatusCode, resp.Status)
}
content, err := io.ReadAll(resp.Body)
if err != nil {
return nil, "", fmt.Errorf("failed to read response body from %s: %w", url, err)
return nil, "", types.NewHTTPFetchError(url, err)
}
// Extract filename from URL path