github.com/rzurga/go-swagger@v0.28.1-0.20211109195225-5d1f453ffa3a/scan/classifier.go (about) 1 // +build !go1.11 2 3 // Copyright 2015 go-swagger maintainers 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package scan 18 19 import ( 20 "fmt" 21 "go/ast" 22 "log" 23 "regexp" 24 25 "golang.org/x/tools/go/loader" 26 ) 27 28 type packageFilter struct { 29 Name string 30 } 31 32 func (pf *packageFilter) Matches(path string) bool { 33 matched, err := regexp.MatchString(pf.Name, path) 34 if err != nil { 35 log.Fatal(err) 36 } 37 return matched 38 } 39 40 type packageFilters []packageFilter 41 42 func (pf packageFilters) HasFilters() bool { 43 return len(pf) > 0 44 } 45 46 func (pf packageFilters) Matches(path string) bool { 47 for _, mod := range pf { 48 if mod.Matches(path) { 49 return true 50 } 51 } 52 return false 53 } 54 55 type classifiedProgram struct { 56 Meta []*ast.File 57 Models []*ast.File 58 Routes []*ast.File 59 Operations []*ast.File 60 Parameters []*ast.File 61 Responses []*ast.File 62 } 63 64 // programClassifier classifies the files of a program into buckets 65 // for processing by a swagger spec generator. This buckets files in 66 // 3 groups: Meta, Models and Operations. 67 // 68 // Each of these buckets is then processed with an appropriate parsing strategy 69 // 70 // When there are Include or Exclude filters provide they are used to limit the 71 // candidates prior to parsing. 72 // The include filters take precedence over the excludes. So when something appears 73 // in both filters it will be included. 74 type programClassifier struct { 75 Includes packageFilters 76 Excludes packageFilters 77 } 78 79 func (pc *programClassifier) Classify(prog *loader.Program) (*classifiedProgram, error) { 80 var cp classifiedProgram 81 for pkg, pkgInfo := range prog.AllPackages { 82 if Debug { 83 log.Printf("analyzing: %s\n", pkg.Path()) 84 } 85 if pc.Includes.HasFilters() { 86 if !pc.Includes.Matches(pkg.Path()) { 87 continue 88 } 89 } else if pc.Excludes.HasFilters() { 90 if pc.Excludes.Matches(pkg.Path()) { 91 continue 92 } 93 } 94 95 for _, file := range pkgInfo.Files { 96 var ro, op, mt, pm, rs, mm bool // only add a particular file once 97 for _, comments := range file.Comments { 98 var seenStruct string 99 for _, cline := range comments.List { 100 if cline != nil { 101 matches := rxSwaggerAnnotation.FindStringSubmatch(cline.Text) 102 if len(matches) > 1 { 103 switch matches[1] { 104 case "route": 105 if !ro { 106 cp.Routes = append(cp.Routes, file) 107 ro = true 108 } 109 case "operation": 110 if !op { 111 cp.Operations = append(cp.Operations, file) 112 op = true 113 } 114 case "model": 115 if !mm { 116 cp.Models = append(cp.Models, file) 117 mm = true 118 } 119 if seenStruct == "" || seenStruct == matches[1] { 120 seenStruct = matches[1] 121 } else { 122 return nil, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text) 123 } 124 case "meta": 125 if !mt { 126 cp.Meta = append(cp.Meta, file) 127 mt = true 128 } 129 case "parameters": 130 if !pm { 131 cp.Parameters = append(cp.Parameters, file) 132 pm = true 133 } 134 if seenStruct == "" || seenStruct == matches[1] { 135 seenStruct = matches[1] 136 } else { 137 return nil, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text) 138 } 139 case "response": 140 if !rs { 141 cp.Responses = append(cp.Responses, file) 142 rs = true 143 } 144 if seenStruct == "" || seenStruct == matches[1] { 145 seenStruct = matches[1] 146 } else { 147 return nil, fmt.Errorf("classifier: already annotated as %s, can't also be %q - %s", seenStruct, matches[1], cline.Text) 148 } 149 case "strfmt", "name", "discriminated", "file", "enum", "default", "alias", "type": 150 // TODO: perhaps collect these and pass along to avoid lookups later on 151 case "allOf": 152 case "ignore": 153 default: 154 return nil, fmt.Errorf("classifier: unknown swagger annotation %q", matches[1]) 155 } 156 } 157 158 } 159 } 160 } 161 } 162 } 163 164 return &cp, nil 165 }