-
Notifications
You must be signed in to change notification settings - Fork 180
/
openurl.go
105 lines (97 loc) · 3.1 KB
/
openurl.go
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
97
98
99
100
101
102
103
104
105
// Copyright 2019 The Go Cloud Development Kit Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package openurl provides helpers for URLMux and URLOpeners in portable APIs.
package openurl
import (
"fmt"
"net/url"
"sort"
"strings"
)
// SchemeMap maps URL schemes to values. The zero value is an empty map, ready for use.
type SchemeMap struct {
api string
m map[string]interface{}
}
// Register registers scheme for value; subsequent calls to FromString or
// FromURL with scheme will return value.
// api is the portable API name (e.g., "blob"); the same value should always
// be passed. It should be in all lowercase.
// typ is the portable type (e.g., "Bucket").
// Register panics if scheme has already been registered.
func (m *SchemeMap) Register(api, typ, scheme string, value interface{}) {
if m.m == nil {
m.m = map[string]interface{}{}
}
if api != strings.ToLower(api) {
panic(fmt.Errorf("api should be lowercase: %q", api))
}
if m.api == "" {
m.api = api
} else if m.api != api {
panic(fmt.Errorf("previously registered using api %q (now %q)", m.api, api))
}
if _, exists := m.m[scheme]; exists {
panic(fmt.Errorf("scheme %q already registered for %s.%s", scheme, api, typ))
}
m.m[scheme] = value
}
// FromString parses urlstr as an URL and looks up the value for the URL's scheme.
func (m *SchemeMap) FromString(typ, urlstr string) (interface{}, *url.URL, error) {
u, err := url.Parse(urlstr)
if err != nil {
return nil, nil, fmt.Errorf("open %s.%s: %v", m.api, typ, err)
}
val, err := m.FromURL(typ, u)
if err != nil {
return nil, nil, err
}
return val, u, nil
}
// FromURL looks up the value for u's scheme.
func (m *SchemeMap) FromURL(typ string, u *url.URL) (interface{}, error) {
scheme := u.Scheme
if scheme == "" {
return nil, fmt.Errorf("open %s.%s: no scheme in URL %q", m.api, typ, u)
}
for _, prefix := range []string{
fmt.Sprintf("%s+%s+", m.api, strings.ToLower(typ)),
fmt.Sprintf("%s+", m.api),
} {
scheme = strings.TrimPrefix(scheme, prefix)
}
v, ok := m.m[scheme]
if !ok {
return nil, fmt.Errorf("open %s.%s: no driver registered for %q for URL %q; available schemes: %v", m.api, typ, scheme, u, strings.Join(m.Schemes(), ", "))
}
return v, nil
}
// Schemes returns a sorted slice of the registered schemes.
func (m *SchemeMap) Schemes() []string {
var schemes []string
for s := range m.m {
schemes = append(schemes, s)
}
sort.Strings(schemes)
return schemes
}
// ValidScheme returns true if scheme has been registered.
func (m *SchemeMap) ValidScheme(scheme string) bool {
for s := range m.m {
if scheme == s {
return true
}
}
return false
}