1package url
2
3import "strings"
4
5// ParseBangShortcut extracts a bang shortcut from input.
6// Input must start with "!" followed by shortcut key and a space.
7// Returns (shortcutKey, query, found).
8//
9// Examples:
10//
11// "!g golang" → ("g", "golang", true)
12// "!ddg test" → ("ddg", "test", true)
13// "!gh repo name" → ("gh", "repo name", true)
14// "!g" → ("", "", false) - no query
15// "plain text" → ("", "", false)
16// "test !g" → ("", "", false) - bang not at start
17func ParseBangShortcut(input string) (shortcut, query string, found bool) {
18 if !strings.HasPrefix(input, "!") {
19 return "", "", false
20 }
21
22 // Find the space separating shortcut from query
23 spaceIdx := strings.Index(input, " ")
24 if spaceIdx == -1 || spaceIdx == 1 {
25 // No space found or empty shortcut ("! query")
26 return "", "", false
27 }
28
29 shortcut = input[1:spaceIdx]
30 query = strings.TrimSpace(input[spaceIdx+1:])
31
32 if query == "" {
33 return "", "", false
34 }
35
36 return shortcut, query, true
37}
38
39// BuildSearchURL constructs a URL from user input, handling bang shortcuts.
40// It checks for bang shortcuts first, then URL-like input, then falls back to default search.
41//
42// Parameters:
43// - input: user input (e.g., "!g golang", "example.com", "search query")
44// - shortcutURLs: map of shortcut keys to URL templates (e.g., {"g": "https://google.com/search?q=%s"})
45// - defaultSearch: default search engine URL template (e.g., "https://duckduckgo.com/?q=%s")
46//
47// Returns the resolved URL.
48func BuildSearchURL(input string, shortcutURLs map[string]string, defaultSearch string) string {
49 if input == "" {
50 return ""
51 }
52
53 // Check for bang shortcut (e.g., "!g query")
54 if shortcutKey, query, found := ParseBangShortcut(input); found {
55 if urlTemplate, ok := shortcutURLs[shortcutKey]; ok {
56 return strings.Replace(urlTemplate, "%s", query, 1)
57 }
58 // Unknown bang falls through to default search with original input
59 }
60
61 // Check if it looks like a URL
62 if LooksLikeURL(input) {
63 return Normalize(input)
64 }
65
66 // Use default search
67 if defaultSearch != "" {
68 return strings.Replace(defaultSearch, "%s", input, 1)
69 }
70
71 return input
72}