package main import ( "flag" "fmt" "html/template" "log" "net/http" "sort" "sync" "time" ) type TextSearchResult struct { URL string Header string Description string Source string } var debugMode bool func init() { flag.BoolVar(&debugMode, "debug", false, "enable debug mode") flag.Parse() } func HandleTextSearch(w http.ResponseWriter, query, safe, lang string) { startTime := time.Now() var combinedResults []TextSearchResult var resultMap = make(map[string]TextSearchResult) var wg sync.WaitGroup var mu sync.Mutex resultsChan := make(chan []TextSearchResult) searchFuncs := []struct { Func func(string, string, string) ([]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) ([]TextSearchResult, error), source string) { defer wg.Done() results, err := searchFunc(query, safe, lang) 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() for _, result := range results { existingResult, exists := resultMap[result.URL] if !exists || shouldReplace(existingResult.Source, result.Source) { resultMap[result.URL] = result } if debugMode { log.Printf("Result from %s: %+v\n", result.Source, result) } } mu.Unlock() } // Convert the map back to a slice for _, result := range resultMap { combinedResults = append(combinedResults, result) } // Custom sorting: Google first, DuckDuckGo second, Qwant third sort.SliceStable(combinedResults, func(i, j int) bool { return sourceOrder(combinedResults[i].Source) < sourceOrder(combinedResults[j].Source) }) displayResults(w, combinedResults, query, lang, time.Since(startTime).Seconds()) } func shouldReplace(existingSource, newSource string) bool { return sourceOrder(newSource) < sourceOrder(existingSource) } func sourceOrder(source string) int { switch source { case "Qwant": return 1 case "DuckDuckGo": return 2 case "Google": return 3 default: return 4 } } func displayResults(w http.ResponseWriter, results []TextSearchResult, query, lang string, elapsed float64) { tmpl, err := template.ParseFiles("templates/text.html") if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } data := struct { Results []TextSearchResult Query string Fetched string LanguageOptions []LanguageOption CurrentLang string }{ Results: results, Query: query, Fetched: fmt.Sprintf("%.2f seconds", elapsed), LanguageOptions: languageOptions, CurrentLang: lang, } err = tmpl.Execute(w, data) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }