package main import ( "fmt" "sync" "time" ) // TextSearchResult represents a single search result item. type TextSearchResult struct { URL string Header string Description string Source string } // CacheKey represents the key used to store search results in the cache. type CacheKey struct { Query string Page int Safe string Lang string } // CachedItem represents an item stored in the cache with an expiration time. type CachedItem struct { Results []TextSearchResult StoredTime time.Time } // ResultsCache is a thread-safe map for caching search results by composite keys. type ResultsCache struct { mu sync.Mutex results map[string]CachedItem expiration time.Duration } // NewResultsCache creates a new ResultsCache with a specified expiration duration. func NewResultsCache(expiration time.Duration) *ResultsCache { return &ResultsCache{ results: make(map[string]CachedItem), expiration: expiration, } } // Get retrieves the results for a given key from the cache. func (rc *ResultsCache) Get(key CacheKey) ([]TextSearchResult, bool) { rc.mu.Lock() defer rc.mu.Unlock() item, exists := rc.results[rc.keyToString(key)] if !exists { return nil, false } // Check if the item has expired if time.Since(item.StoredTime) > rc.expiration { delete(rc.results, rc.keyToString(key)) return nil, false } return item.Results, true } // Set stores the results for a given key in the cache. func (rc *ResultsCache) Set(key CacheKey, results []TextSearchResult) { rc.mu.Lock() defer rc.mu.Unlock() rc.results[rc.keyToString(key)] = CachedItem{ Results: results, StoredTime: time.Now(), } } // keyToString converts a CacheKey to a string representation. func (rc *ResultsCache) keyToString(key CacheKey) string { return fmt.Sprintf("%s|%d|%s|%s", key.Query, key.Page, key.Safe, key.Lang) }