diff --git a/cmd/swag/main.go b/cmd/swag/main.go index 6ba970561..1b2d9bb39 100644 --- a/cmd/swag/main.go +++ b/cmd/swag/main.go @@ -34,6 +34,7 @@ const ( parseGoListFlag = "parseGoList" quietFlag = "quiet" tagsFlag = "tags" + parseExtensionFlag = "parseExtension" ) var initFlags = []cli.Flag{ @@ -129,6 +130,11 @@ var initFlags = []cli.Flag{ Value: true, Usage: "Parse dependency via 'go list'", }, + &cli.StringFlag{ + Name: parseExtensionFlag, + Value: "", + Usage: "Parse only those operations that match given extension", + }, &cli.StringFlag{ Name: tagsFlag, Aliases: []string{"t"}, @@ -158,6 +164,7 @@ func initAction(ctx *cli.Context) error { return gen.New().Build(&gen.Config{ SearchDir: ctx.String(searchDirFlag), Excludes: ctx.String(excludeFlag), + ParseExtension: ctx.String(parseExtensionFlag), MainAPIFile: ctx.String(generalInfoFlag), PropNamingStrategy: strategy, OutputDir: ctx.String(outputFlag), diff --git a/gen/gen.go b/gen/gen.go index 87e7d0d20..ddd02f751 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -72,6 +72,9 @@ type Config struct { // excludes dirs and files in SearchDir,comma separated Excludes string + // outputs only specific extension + ParseExtension string + // OutputDir represents the output directory for all the generated files OutputDir string @@ -167,6 +170,7 @@ func (g *Gen) Build(config *Config) error { swag.SetMarkdownFileDirectory(config.MarkdownFilesDir), swag.SetDebugger(config.Debugger), swag.SetExcludedDirsAndFiles(config.Excludes), + swag.SetParseExtension(config.ParseExtension), swag.SetCodeExamplesDirectory(config.CodeExampleFilesDir), swag.SetStrict(config.Strict), swag.SetOverrides(overrides), diff --git a/parser.go b/parser.go index ea77311d5..261ba6c55 100644 --- a/parser.go +++ b/parser.go @@ -137,6 +137,9 @@ type Parser struct { // excludes excludes dirs and files in SearchDir excludes map[string]struct{} + // tells parser to include only specific extension + parseExtension string + // debugging output goes here debug Debugger @@ -265,6 +268,13 @@ func SetTags(include string) func(*Parser) { } } +// SetParseExtension parses only those operations which match given extension +func SetParseExtension(parseExtension string) func(*Parser) { + return func(p *Parser) { + p.parseExtension = parseExtension + } +} + // SetStrict sets whether swag should error or warn when it detects cases which are most likely user errors. func SetStrict(strict bool) func(*Parser) { return func(p *Parser) { @@ -828,12 +838,29 @@ func (parser *Parser) matchTags(comments []*ast.Comment) (match bool) { return true } +func matchExtension(extensionToMatch string, comments []*ast.Comment) (match bool) { + if len(extensionToMatch) != 0 { + for _, comment := range comments { + commentLine := strings.TrimSpace(strings.TrimLeft(comment.Text, "/")) + fields := FieldsByAnySpace(commentLine, 2) + lowerAttribute := strings.ToLower(fields[0]) + + if lowerAttribute == fmt.Sprintf("@x-%s", strings.ToLower(extensionToMatch)) { + return true + } + } + return false + } + return true +} + // ParseRouterAPIInfo parses router api info for given astFile. func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error { for _, astDescription := range astFile.Decls { astDeclaration, ok := astDescription.(*ast.FuncDecl) if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil { - if parser.matchTags(astDeclaration.Doc.List) { + if parser.matchTags(astDeclaration.Doc.List) && + matchExtension(parser.parseExtension, astDeclaration.Doc.List) { // for per 'function' comment, create a new 'Operation' object operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir)) for _, comment := range astDeclaration.Doc.List { diff --git a/parser_test.go b/parser_test.go index c7e5bacb1..0876b94e9 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3878,3 +3878,51 @@ func TestParser_matchTags(t *testing.T) { }) } } + +func TestParser_parseExtension(t *testing.T) { + + src, err := os.ReadFile("testdata/parseExtension/parseExtension.go") + assert.NoError(t, err) + + f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments) + assert.NoError(t, err) + + tests := []struct { + name string + parser *Parser + expectedPaths map[string]bool + }{ + { + name: "when no flag is set, everything is exported", + parser: New(), + expectedPaths: map[string]bool{"/without-extension": true, "/with-another-extension": true, "/with-correct-extension": true}, + }, + { + name: "when nonexistent flag is set, nothing is exported", + parser: New(SetParseExtension("nonexistent-extension-filter")), + expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": false}, + }, + { + name: "when correct flag is set, only that Path is exported", + parser: New(SetParseExtension("google-backend")), + expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": true}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = tt.parser.ParseRouterAPIInfo("", f) + assert.NoError(t, err) + for p, isExpected := range tt.expectedPaths { + _, ok := tt.parser.swagger.Paths.Paths[p] + assert.Equal(t, isExpected, ok) + } + + for p := range tt.parser.swagger.Paths.Paths { + _, isExpected := tt.expectedPaths[p] + assert.Equal(t, isExpected, true) + } + }) + + } +} diff --git a/testdata/parseExtension/parseExtension.go b/testdata/parseExtension/parseExtension.go new file mode 100644 index 000000000..9948c7a25 --- /dev/null +++ b/testdata/parseExtension/parseExtension.go @@ -0,0 +1,12 @@ +package main + +// @Router /without-extension [get] +func Fun() {} + +// @Router /with-another-extension [get] +// @x-another-extension {"address": "http://backend"} +func Fun2() {} + +// @Router /with-correct-extension [get] +// @x-google-backend {"address": "http://backend"} +func Fun3() {}