summaryrefslogtreecommitdiff
path: root/internal/image
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-03 08:03:27 +0300
committerPaul Buetow <paul@buetow.org>2026-04-03 08:03:27 +0300
commitf4bc35c0a24766c8e9a155cbad5ffdaf088ec458 (patch)
treea9f1bb488bc7473549cb6e3552c531154841f0ef /internal/image
parent63bd86d8046949e80e5b1122a1fcf717f51915c6 (diff)
task 00o: split ImageSearcher into ImageSearcher + AttributionProvider (ISP)
Introduce AttributionProvider interface with GetAttribution() separate from ImageSearcher so callers only needing search/download don't carry attribution. ImageClient composes both. Downloader.searcher and NewDownloader now take ImageClient since attribution is needed when saving downloaded files. Compile-time assertions updated to ImageClient. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/image')
-rw-r--r--internal/image/download.go8
-rw-r--r--internal/image/nanobanana.go4
-rw-r--r--internal/image/openai.go5
-rw-r--r--internal/image/search.go27
4 files changed, 31 insertions, 13 deletions
diff --git a/internal/image/download.go b/internal/image/download.go
index 2f04359..5c9b020 100644
--- a/internal/image/download.go
+++ b/internal/image/download.go
@@ -29,14 +29,16 @@ func DefaultDownloadOptions() *DownloadOptions {
}
}
-// Downloader handles image downloads from search results
+// Downloader handles image downloads from search results.
+// It requires ImageClient (ImageSearcher + AttributionProvider) because it
+// both downloads images and embeds attribution text in saved files.
type Downloader struct {
- searcher ImageSearcher
+ searcher ImageClient
options *DownloadOptions
}
// NewDownloader creates a new image downloader
-func NewDownloader(searcher ImageSearcher, options *DownloadOptions) *Downloader {
+func NewDownloader(searcher ImageClient, options *DownloadOptions) *Downloader {
if options == nil {
options = DefaultDownloadOptions()
}
diff --git a/internal/image/nanobanana.go b/internal/image/nanobanana.go
index 1a3d6ad..4e99dfa 100644
--- a/internal/image/nanobanana.go
+++ b/internal/image/nanobanana.go
@@ -48,7 +48,9 @@ type NanoBananaClient struct {
PromptCallback func(prompt string)
}
-var _ ImageSearcher = (*NanoBananaClient)(nil)
+// Compile-time check that NanoBananaClient implements the full ImageClient interface
+// (ImageSearcher + AttributionProvider).
+var _ ImageClient = (*NanoBananaClient)(nil)
var newNanoBananaClient = genai.NewClient
var nanoBananaGenerateText = func(ctx context.Context, c *NanoBananaClient, model, systemPrompt, userPrompt string, temperature float32, maxOutputTokens int32) (string, error) {
diff --git a/internal/image/openai.go b/internal/image/openai.go
index 8623fcd..c277406 100644
--- a/internal/image/openai.go
+++ b/internal/image/openai.go
@@ -13,8 +13,9 @@ import (
"github.com/sashabaranov/go-openai"
)
-// Compile-time check that OpenAIClient implements the ImageSearcher interface.
-var _ ImageSearcher = (*OpenAIClient)(nil)
+// Compile-time check that OpenAIClient implements the full ImageClient interface
+// (ImageSearcher + AttributionProvider).
+var _ ImageClient = (*OpenAIClient)(nil)
// imageHTTPClient is a shared HTTP client with a generous timeout for image
// downloads. http.DefaultClient has no timeout, which can block goroutines
diff --git a/internal/image/search.go b/internal/image/search.go
index 9be72ed..be20731 100644
--- a/internal/image/search.go
+++ b/internal/image/search.go
@@ -43,21 +43,34 @@ func DefaultSearchOptions(query string) *SearchOptions {
}
}
-// ImageSearcher defines the interface for image search providers
+// AttributionProvider returns required attribution text for a search result.
+// Kept separate from ImageSearcher so callers that only need attribution
+// do not depend on Search/Download/Name.
+type AttributionProvider interface {
+ GetAttribution(result *SearchResult) string
+}
+
+// ImageSearcher defines the interface for image search providers.
+// Providers that also carry attribution text implement AttributionProvider
+// in addition to this interface.
type ImageSearcher interface {
- // Search performs an image search with the given options
+ // Search performs an image search with the given options.
Search(ctx context.Context, opts *SearchOptions) ([]SearchResult, error)
- // Download downloads an image from the given URL
+ // Download downloads an image from the given URL.
Download(ctx context.Context, url string) (io.ReadCloser, error)
- // GetAttribution returns the required attribution text for an image
- GetAttribution(result *SearchResult) string
-
- // Name returns the name of the search provider
+ // Name returns the name of the search provider.
Name() string
}
+// ImageClient combines ImageSearcher and AttributionProvider for callers
+// that need full provider capabilities (search, download, attribution).
+type ImageClient interface {
+ ImageSearcher
+ AttributionProvider
+}
+
// SearchError represents an error from an image search provider
type SearchError struct {
Provider string