~bigbes/huntsman

ref: 783841b91eafd678cb3895cfcc8dfd89f290ece7 huntsman/internal/domain/search/opensearch.go -rw-r--r-- 3.2 KiB
783841b9 — Eugene Blikh Initial commit: multi-provider search router 6 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package search

import (
	"encoding/xml"
	"fmt"
	"strings"
)

// OpenSearchDescription is the root <OpenSearchDescription> element of an
// OSD 1.1 document. Browsers (Firefox, Chromium) parse this to register
// a search engine; the {searchTerms} placeholder in URL templates is
// substituted by the browser at search time.
//
// Spec: https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md
type OpenSearchDescription struct {
	XMLName        xml.Name `xml:"OpenSearchDescription"`
	XMLNS          string   `xml:"xmlns,attr"`
	ShortName      string   `xml:"ShortName"`
	Description    string   `xml:"Description"`
	InputEncoding  string   `xml:"InputEncoding"`
	Image          *Image   `xml:"Image,omitempty"`
	URLs           []URL    `xml:"Url"`
	SearchForm     string   `xml:"SearchForm,omitempty"`
	Developer      string   `xml:"Developer,omitempty"`
	Attribution    string   `xml:"Attribution,omitempty"`
	SyndicationRgt string   `xml:"SyndicationRight,omitempty"`
	AdultContent   string   `xml:"AdultContent,omitempty"`
}

// URL is a single <Url> element pointing at a search template.
type URL struct {
	Type     string `xml:"type,attr"`
	Template string `xml:"template,attr"`
	Method   string `xml:"method,attr,omitempty"`
}

// Image is a <Image> element for the search engine icon.
type Image struct {
	Height int    `xml:"height,attr"`
	Width  int    `xml:"width,attr"`
	Type   string `xml:"type,attr"`
	Value  string `xml:",chardata"`
}

const xmlNS = "http://a9.com/-/spec/opensearch/1.1/"

// DescriptionForProvider builds an OSD document that points the browser
// directly at the upstream provider — useful when the user wants to add
// e.g. urbandictionary as a standalone search engine.
func DescriptionForProvider(p Provider) OpenSearchDescription {
	osd := OpenSearchDescription{
		XMLNS:         xmlNS,
		ShortName:     p.Name,
		Description:   p.Description,
		InputEncoding: "UTF-8",
		URLs: []URL{{
			Type: "text/html",
			// OpenSearch uses {searchTerms}; convert from our internal {q}.
			Template: strings.ReplaceAll(p.SearchURL, "{q}", "{searchTerms}"),
		}},
		SearchForm: p.HomeURL,
	}
	if p.IconURL != "" {
		osd.Image = &Image{Height: 16, Width: 16, Type: "image/x-icon", Value: p.IconURL}
	}
	return osd
}

// DescriptionForRouter builds an OSD document that points the browser at
// this service's own /search endpoint, so prefix routing ("ud foo") works
// from the browser's address bar.
//
// publicURL must be the externally-visible base URL (no trailing slash).
func DescriptionForRouter(publicURL string) OpenSearchDescription {
	publicURL = strings.TrimRight(publicURL, "/")
	return OpenSearchDescription{
		XMLNS:         xmlNS,
		ShortName:     "huntsman",
		Description:   "Multi-provider search router (ud, gh, steam)",
		InputEncoding: "UTF-8",
		URLs: []URL{{
			Type:     "text/html",
			Template: fmt.Sprintf("%s/search?q={searchTerms}", publicURL),
		}},
		SearchForm: publicURL + "/",
	}
}

// Marshal serializes an OSD document with the standard XML prolog.
func Marshal(osd OpenSearchDescription) ([]byte, error) {
	body, err := xml.MarshalIndent(osd, "", "  ")
	if err != nil {
		return nil, err
	}
	return append([]byte(xml.Header), body...), nil
}