// text.go package main import ( "flag" "fmt" "html/template" "log" "net/http" "sort" "sync" "time" ) var ( debugMode bool resultsCache = NewResultsCache() ) func init() { flag.BoolVar(&debugMode, "debug", false, "enable debug mode") flag.Parse() } func HandleTextSearch(w http.ResponseWriter, query, safe, lang string, page int) { startTime := time.Now() const resultsPerPage = 10 cacheKey := CacheKey{Query: query, Page: page, Safe: safe, Lang: lang} // Try to get results from cache combinedResults, exists := resultsCache.Get(cacheKey) if !exists { // Fetch results for the current page combinedResults = fetchAndCacheResults(query, safe, lang, page, resultsPerPage) resultsCache.Set(cacheKey, combinedResults) } // Pre-fetch and cache results for the next page nextPageResults := fetchAndCacheResults(query, safe, lang, page+1, resultsPerPage) resultsCache.Set(CacheKey{Query: query, Page: page + 1, Safe: safe, Lang: lang}, nextPageResults) hasPrevPage := page > 1 hasNextPage := len(nextPageResults) > 0 displayResults(w, combinedResults, query, lang, time.Since(startTime).Seconds(), page, hasPrevPage, hasNextPage) } func fetchAndCacheResults(query, safe, lang string, page, resultsPerPage int) []TextSearchResult { var combinedResults []TextSearchResult var wg sync.WaitGroup var mu sync.Mutex resultsChan := make(chan []TextSearchResult) searchFuncs := []struct { Func func(string, string, string, int) ([]TextSearchResult, error) Source string }{ {PerformGoogleTextSearch, "Google"}, {PerformDuckDuckGoTextSearch, "DuckDuckGo"}, {PerformQwantTextSearch, "Qwant"}, } wg.Add(len(searchFuncs)) for _, searchFunc := range searchFuncs { go func(searchFunc func(string, string, string, int) ([]TextSearchResult, error), source string) { defer wg.Done() results, err := searchFunc(query, safe, lang, page) if err == nil { for i := range results { results[i].Source = source } resultsChan <- results } else { log.Printf("Error performing search from %s: %v", source, err) } }(searchFunc.Func, searchFunc.Source) } go func() { wg.Wait() close(resultsChan) }() for results := range resultsChan { mu.Lock() combinedResults = append(combinedResults, results...) mu.Unlock() } // Sort combinedResults by source priority: Google first, DuckDuckGo second, Qwant third sort.SliceStable(combinedResults, func(i, j int) bool { return sourceOrder(combinedResults[i].Source) < sourceOrder(combinedResults[j].Source) }) // Paginate results startIndex := (page - 1) * resultsPerPage endIndex := startIndex + resultsPerPage // Ensure startIndex and endIndex are within bounds if startIndex >= len(combinedResults) { return []TextSearchResult{} } if endIndex > len(combinedResults) { endIndex = len(combinedResults) } return combinedResults[startIndex:endIndex] } func sourceOrder(source string) int { switch source { case "Google": return 1 case "DuckDuckGo": return 2 case "Qwant": return 3 default: return 4 } } func displayResults(w http.ResponseWriter, results []TextSearchResult, query, lang string, elapsed float64, page int, hasPrevPage, hasNextPage bool) { tmpl, err := template.New("text.html").Funcs(template.FuncMap{ "sub": func(a, b int) int { return a - b }, "add": func(a, b int) int { return a + b }, }).ParseFiles("templates/text.html") if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } data := struct { Results []TextSearchResult Query string Fetched string Page int HasPrevPage bool HasNextPage bool LanguageOptions []LanguageOption CurrentLang string }{ Results: results, Query: query, Fetched: fmt.Sprintf("%.2f seconds", elapsed), Page: page, HasPrevPage: hasPrevPage, HasNextPage: hasNextPage, LanguageOptions: languageOptions, CurrentLang: lang, } err = tmpl.Execute(w, data) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }