github.com/cornelk/go-cloud@v0.17.1/internal/openurl/openurl.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package openurl provides helpers for URLMux and URLOpeners in portable APIs. 16 package openurl // import "github.com/cornelk/go-cloud/internal/openurl" 17 18 import ( 19 "fmt" 20 "net/url" 21 "sort" 22 "strings" 23 ) 24 25 // SchemeMap maps URL schemes to values. The zero value is an empty map, ready for use. 26 type SchemeMap struct { 27 api string 28 m map[string]interface{} 29 } 30 31 // Register registers scheme for value; subsequent calls to FromString or 32 // FromURL with scheme will return value. 33 // api is the portable API name (e.g., "blob"); the same value should always 34 // be passed. It should be in all lowercase. 35 // typ is the portable type (e.g., "Bucket"). 36 // Register panics if scheme has already been registered. 37 func (m *SchemeMap) Register(api, typ, scheme string, value interface{}) { 38 if m.m == nil { 39 m.m = map[string]interface{}{} 40 } 41 if api != strings.ToLower(api) { 42 panic(fmt.Errorf("api should be lowercase: %q", api)) 43 } 44 if m.api == "" { 45 m.api = api 46 } else if m.api != api { 47 panic(fmt.Errorf("previously registered using api %q (now %q)", m.api, api)) 48 } 49 if _, exists := m.m[scheme]; exists { 50 panic(fmt.Errorf("scheme %q already registered for %s.%s", scheme, api, typ)) 51 } 52 m.m[scheme] = value 53 } 54 55 // FromString parses urlstr as an URL and looks up the value for the URL's scheme. 56 func (m *SchemeMap) FromString(typ, urlstr string) (interface{}, *url.URL, error) { 57 u, err := url.Parse(urlstr) 58 if err != nil { 59 return nil, nil, fmt.Errorf("open %s.%s: %v", m.api, typ, err) 60 } 61 val, err := m.FromURL(typ, u) 62 if err != nil { 63 return nil, nil, err 64 } 65 return val, u, nil 66 } 67 68 // FromURL looks up the value for u's scheme. 69 func (m *SchemeMap) FromURL(typ string, u *url.URL) (interface{}, error) { 70 scheme := u.Scheme 71 if scheme == "" { 72 return nil, fmt.Errorf("open %s.%s: no scheme in URL %q", m.api, typ, u) 73 } 74 for _, prefix := range []string{ 75 fmt.Sprintf("%s+%s+", m.api, strings.ToLower(typ)), 76 fmt.Sprintf("%s+", m.api), 77 } { 78 scheme = strings.TrimPrefix(scheme, prefix) 79 } 80 v, ok := m.m[scheme] 81 if !ok { 82 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(), ", ")) 83 } 84 return v, nil 85 } 86 87 // Schemes returns a sorted slice of the registered schemes. 88 func (m *SchemeMap) Schemes() []string { 89 var schemes []string 90 for s := range m.m { 91 schemes = append(schemes, s) 92 } 93 sort.Strings(schemes) 94 return schemes 95 } 96 97 // ValidScheme returns true iff scheme has been registered. 98 func (m *SchemeMap) ValidScheme(scheme string) bool { 99 for s := range m.m { 100 if scheme == s { 101 return true 102 } 103 } 104 return false 105 }