1package loads 2 3import ( 4 "encoding/json" 5 "errors" 6 "net/url" 7 8 "github.com/go-openapi/spec" 9 "github.com/go-openapi/swag" 10) 11 12var ( 13 // Default chain of loaders, defined at the package level. 14 // 15 // By default this matches json and yaml documents. 16 // 17 // May be altered with AddLoader(). 18 loaders *loader 19) 20 21func init() { 22 jsonLoader := &loader{ 23 DocLoaderWithMatch: DocLoaderWithMatch{ 24 Match: func(pth string) bool { 25 return true 26 }, 27 Fn: JSONDoc, 28 }, 29 } 30 31 loaders = jsonLoader.WithHead(&loader{ 32 DocLoaderWithMatch: DocLoaderWithMatch{ 33 Match: swag.YAMLMatcher, 34 Fn: swag.YAMLDoc, 35 }, 36 }) 37 38 // sets the global default loader for go-openapi/spec 39 spec.PathLoader = loaders.Load 40} 41 42// DocLoader represents a doc loader type 43type DocLoader func(string) (json.RawMessage, error) 44 45// DocMatcher represents a predicate to check if a loader matches 46type DocMatcher func(string) bool 47 48// DocLoaderWithMatch describes a loading function for a given extension match. 49type DocLoaderWithMatch struct { 50 Fn DocLoader 51 Match DocMatcher 52} 53 54// NewDocLoaderWithMatch builds a DocLoaderWithMatch to be used in load options 55func NewDocLoaderWithMatch(fn DocLoader, matcher DocMatcher) DocLoaderWithMatch { 56 return DocLoaderWithMatch{ 57 Fn: fn, 58 Match: matcher, 59 } 60} 61 62type loader struct { 63 DocLoaderWithMatch 64 Next *loader 65} 66 67// WithHead adds a loader at the head of the current stack 68func (l *loader) WithHead(head *loader) *loader { 69 if head == nil { 70 return l 71 } 72 head.Next = l 73 return head 74} 75 76// WithNext adds a loader at the trail of the current stack 77func (l *loader) WithNext(next *loader) *loader { 78 l.Next = next 79 return next 80} 81 82// Load the raw document from path 83func (l *loader) Load(path string) (json.RawMessage, error) { 84 _, erp := url.Parse(path) 85 if erp != nil { 86 return nil, erp 87 } 88 89 var lastErr error = errors.New("no loader matched") // default error if no match was found 90 for ldr := l; ldr != nil; ldr = ldr.Next { 91 if ldr.Match != nil && !ldr.Match(path) { 92 continue 93 } 94 95 // try then move to next one if there is an error 96 b, err := ldr.Fn(path) 97 if err == nil { 98 return b, nil 99 } 100 101 lastErr = err 102 } 103 104 return nil, lastErr 105} 106 107// JSONDoc loads a json document from either a file or a remote url 108func JSONDoc(path string) (json.RawMessage, error) { 109 data, err := swag.LoadFromFileOrHTTP(path) 110 if err != nil { 111 return nil, err 112 } 113 return json.RawMessage(data), nil 114} 115 116// AddLoader for a document, executed before other previously set loaders. 117// 118// This sets the configuration at the package level. 119// 120// NOTE: 121// * this updates the default loader used by github.com/go-openapi/spec 122// * since this sets package level globals, you shouln't call this concurrently 123// 124func AddLoader(predicate DocMatcher, load DocLoader) { 125 loaders = loaders.WithHead(&loader{ 126 DocLoaderWithMatch: DocLoaderWithMatch{ 127 Match: predicate, 128 Fn: load, 129 }, 130 }) 131 132 // sets the global default loader for go-openapi/spec 133 spec.PathLoader = loaders.Load 134} 135