From f4bc35c0a24766c8e9a155cbad5ffdaf088ec458 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 3 Apr 2026 08:03:27 +0300 Subject: 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 --- internal/image/download.go | 8 +++++--- internal/image/nanobanana.go | 4 +++- internal/image/openai.go | 5 +++-- internal/image/search.go | 27 ++++++++++++++++++++------- 4 files changed, 31 insertions(+), 13 deletions(-) (limited to 'internal/image') 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 -- cgit v1.2.3