Skip to content

Commit 59488e3

Browse files
authored
Merge 856e911 into 3c5e486
2 parents 3c5e486 + 856e911 commit 59488e3

File tree

10 files changed

+262
-31
lines changed

10 files changed

+262
-31
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ dist
22
testdata/simple*/docs
33
testdata/quotes/docs
44
testdata/quotes/quotes.so
5+
testdata/delims/docs
6+
testdata/delims/delims.so
57
example/basic/docs/*
68
example/celler/docs/*
79
cover.out

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ OPTIONS:
104104
--overridesFile value File to read global type overrides from. (default: ".swaggo")
105105
--parseGoList Parse dependency via 'go list' (default: true)
106106
--tags value, -t value A comma-separated list of tags to filter the APIs for which the documentation is generated.Special case if the tag is prefixed with the '!' character then the APIs with that tag will be excluded
107+
--templateDelims value, --td value Provide custom delimeters for Go template generation. The format is leftDelim,rightDelim. For example: "[[,]]"
107108
--collectionFormat value, --cf value Set default collection format (default: "csv")
108109
--help, -h show help (default: false)
109110
```
@@ -908,6 +909,18 @@ By default `swag` command generates Swagger specification in three different fil
908909
909910
If you would like to limit a set of file types which should be generated you can use `--outputTypes` (short `-ot`) flag. Default value is `go,json,yaml` - output types separated with comma. To limit output only to `go` and `yaml` files, you would write `go,yaml`. With complete command that would be `swag init --outputTypes go,yaml`.
910911
912+
### Change the default Go Template action delimiters
913+
[#980](https://github.com/swaggo/swag/issues/980)
914+
[#1177](https://github.com/swaggo/swag/issues/1177)
915+
916+
If your swagger annotations or struct fields contain "{{" or "}}", the template generation will most likely fail, as these are the default delimiters for [go templates](https://pkg.go.dev/text/template#Template.Delims).
917+
918+
To make the generation work properly, you can change the default delimiters with `-td`. For example:
919+
```console
920+
swag init -g http/api.go -td "[[,]]"
921+
```
922+
The new delimiter is a string with the format "`<left delimiter>`,`<right delimiter>`".
923+
911924
## About the Project
912925
This project was inspired by [yvasiyarov/swagger](https://github.com/yvasiyarov/swagger) but we simplified the usage and added support a variety of [web frameworks](#supported-web-frameworks). Gopher image source is [tenntenn/gopher-stickers](https://github.com/tenntenn/gopher-stickers). It has licenses [creative commons licensing](http://creativecommons.org/licenses/by/3.0/deed.en).
913926
## Contributors

cmd/swag/main.go

+21
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const (
3535
quietFlag = "quiet"
3636
tagsFlag = "tags"
3737
parseExtensionFlag = "parseExtension"
38+
templateDelimsFlag = "templateDelims"
3839
packageName = "packageName"
3940
collectionFormatFlag = "collectionFormat"
4041
)
@@ -143,6 +144,12 @@ var initFlags = []cli.Flag{
143144
Value: "",
144145
Usage: "A comma-separated list of tags to filter the APIs for which the documentation is generated.Special case if the tag is prefixed with the '!' character then the APIs with that tag will be excluded",
145146
},
147+
&cli.StringFlag{
148+
Name: templateDelimsFlag,
149+
Aliases: []string{"td"},
150+
Value: "",
151+
Usage: "Provide custom delimeters for Go template generation. The format is leftDelim,rightDelim. For example: \"[[,]]\"",
152+
},
146153
&cli.StringFlag{
147154
Name: packageName,
148155
Value: "",
@@ -165,6 +172,18 @@ func initAction(ctx *cli.Context) error {
165172
return fmt.Errorf("not supported %s propertyStrategy", strategy)
166173
}
167174

175+
leftDelim, rightDelim := "{{", "}}"
176+
177+
if ctx.IsSet(templateDelimsFlag) {
178+
delims := strings.Split(ctx.String(templateDelimsFlag), ",")
179+
if len(delims) != 2 {
180+
return fmt.Errorf("exactly two template delimeters must be provided, comma separated")
181+
} else if delims[0] == delims[1] {
182+
return fmt.Errorf("template delimiters must be different")
183+
}
184+
leftDelim, rightDelim = strings.TrimSpace(delims[0]), strings.TrimSpace(delims[1])
185+
}
186+
168187
outputTypes := strings.Split(ctx.String(outputTypesFlag), ",")
169188
if len(outputTypes) == 0 {
170189
return fmt.Errorf("no output types specified")
@@ -199,6 +218,8 @@ func initAction(ctx *cli.Context) error {
199218
OverridesFile: ctx.String(overridesFileFlag),
200219
ParseGoList: ctx.Bool(parseGoListFlag),
201220
Tags: ctx.String(tagsFlag),
221+
LeftTemplateDelim: leftDelim,
222+
RightTemplateDelim: rightDelim,
202223
PackageName: ctx.String(packageName),
203224
Debugger: logger,
204225
CollectionFormat: collectionFormat,

gen/gen.go

+48-28
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ type Config struct {
127127
// include only tags mentioned when searching, comma separated
128128
Tags string
129129

130+
// LeftTemplateDelim defines the left delimiter for the template generation
131+
LeftTemplateDelim string
132+
133+
// RightTemplateDelim defines the right delimiter for the template generation
134+
RightTemplateDelim string
135+
130136
// PackageName defines package name of generated `docs.go`
131137
PackageName string
132138

@@ -150,6 +156,14 @@ func (g *Gen) Build(config *Config) error {
150156
}
151157
}
152158

159+
if config.LeftTemplateDelim == "" {
160+
config.LeftTemplateDelim = "{{"
161+
}
162+
163+
if config.RightTemplateDelim == "" {
164+
config.RightTemplateDelim = "}}"
165+
}
166+
153167
var overrides map[string]string
154168

155169
if config.OverridesFile != "" {
@@ -377,7 +391,7 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
377391
generator, err := template.New("swagger_info").Funcs(template.FuncMap{
378392
"printDoc": func(v string) string {
379393
// Add schemes
380-
v = "{\n \"schemes\": {{ marshal .Schemes }}," + v[1:]
394+
v = "{\n \"schemes\": " + config.LeftTemplateDelim + " marshal .Schemes " + config.RightTemplateDelim + "," + v[1:]
381395
// Sanitize backticks
382396
return strings.Replace(v, "`", "`+\"`\"+`", -1)
383397
},
@@ -396,16 +410,16 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
396410
Info: &spec.Info{
397411
VendorExtensible: swagger.Info.VendorExtensible,
398412
InfoProps: spec.InfoProps{
399-
Description: "{{escape .Description}}",
400-
Title: "{{.Title}}",
413+
Description: config.LeftTemplateDelim + "escape .Description" + config.RightTemplateDelim,
414+
Title: config.LeftTemplateDelim + ".Title" + config.RightTemplateDelim,
401415
TermsOfService: swagger.Info.TermsOfService,
402416
Contact: swagger.Info.Contact,
403417
License: swagger.Info.License,
404-
Version: "{{.Version}}",
418+
Version: config.LeftTemplateDelim + ".Version" + config.RightTemplateDelim,
405419
},
406420
},
407-
Host: "{{.Host}}",
408-
BasePath: "{{.BasePath}}",
421+
Host: config.LeftTemplateDelim + ".Host" + config.RightTemplateDelim,
422+
BasePath: config.LeftTemplateDelim + ".BasePath" + config.RightTemplateDelim,
409423
Paths: swagger.Paths,
410424
Definitions: swagger.Definitions,
411425
Parameters: swagger.Parameters,
@@ -426,29 +440,33 @@ func (g *Gen) writeGoDoc(packageName string, output io.Writer, swagger *spec.Swa
426440
buffer := &bytes.Buffer{}
427441

428442
err = generator.Execute(buffer, struct {
429-
Timestamp time.Time
430-
Doc string
431-
Host string
432-
PackageName string
433-
BasePath string
434-
Title string
435-
Description string
436-
Version string
437-
InstanceName string
438-
Schemes []string
439-
GeneratedTime bool
443+
Timestamp time.Time
444+
Doc string
445+
Host string
446+
PackageName string
447+
BasePath string
448+
Title string
449+
Description string
450+
Version string
451+
InstanceName string
452+
Schemes []string
453+
GeneratedTime bool
454+
LeftTemplateDelim string
455+
RightTemplateDelim string
440456
}{
441-
Timestamp: time.Now(),
442-
GeneratedTime: config.GeneratedTime,
443-
Doc: string(buf),
444-
Host: swagger.Host,
445-
PackageName: packageName,
446-
BasePath: swagger.BasePath,
447-
Schemes: swagger.Schemes,
448-
Title: swagger.Info.Title,
449-
Description: swagger.Info.Description,
450-
Version: swagger.Info.Version,
451-
InstanceName: config.InstanceName,
457+
Timestamp: time.Now(),
458+
GeneratedTime: config.GeneratedTime,
459+
Doc: string(buf),
460+
Host: swagger.Host,
461+
PackageName: packageName,
462+
BasePath: swagger.BasePath,
463+
Schemes: swagger.Schemes,
464+
Title: swagger.Info.Title,
465+
Description: swagger.Info.Description,
466+
Version: swagger.Info.Version,
467+
InstanceName: config.InstanceName,
468+
LeftTemplateDelim: config.LeftTemplateDelim,
469+
RightTemplateDelim: config.RightTemplateDelim,
452470
})
453471
if err != nil {
454472
return err
@@ -480,6 +498,8 @@ var SwaggerInfo{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }}
480498
Description: {{ printf "%q" .Description}},
481499
InfoInstanceName: {{ printf "%q" .InstanceName }},
482500
SwaggerTemplate: docTemplate{{ if ne .InstanceName "swagger" }}{{ .InstanceName }} {{- end }},
501+
LeftDelim: {{ printf "%q" .LeftTemplateDelim}},
502+
RightDelim: {{ printf "%q" .RightTemplateDelim}},
483503
}
484504
485505
func init() {

gen/gen_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,67 @@ func TestGen_BuildDescriptionWithQuotes(t *testing.T) {
261261
assert.JSONEq(t, string(expectedJSON), jsonOutput)
262262
}
263263

264+
func TestGen_BuildDocCustomDelims(t *testing.T) {
265+
config := &Config{
266+
SearchDir: "../testdata/delims",
267+
MainAPIFile: "./main.go",
268+
OutputDir: "../testdata/delims/docs",
269+
OutputTypes: outputTypes,
270+
MarkdownFilesDir: "../testdata/delims",
271+
InstanceName: "CustomDelims",
272+
LeftTemplateDelim: "{%",
273+
RightTemplateDelim: "%}",
274+
}
275+
276+
require.NoError(t, New().Build(config))
277+
278+
expectedFiles := []string{
279+
filepath.Join(config.OutputDir, "CustomDelims_docs.go"),
280+
filepath.Join(config.OutputDir, "CustomDelims_swagger.json"),
281+
filepath.Join(config.OutputDir, "CustomDelims_swagger.yaml"),
282+
}
283+
for _, expectedFile := range expectedFiles {
284+
if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
285+
require.NoError(t, err)
286+
}
287+
}
288+
289+
cmd := exec.Command("go", "build", "-buildmode=plugin", "github.com/swaggo/swag/testdata/delims")
290+
291+
cmd.Dir = config.SearchDir
292+
293+
output, err := cmd.CombinedOutput()
294+
if err != nil {
295+
require.NoError(t, err, string(output))
296+
}
297+
298+
p, err := plugin.Open(filepath.Join(config.SearchDir, "delims.so"))
299+
if err != nil {
300+
require.NoError(t, err)
301+
}
302+
303+
defer os.Remove("delims.so")
304+
305+
readDoc, err := p.Lookup("ReadDoc")
306+
if err != nil {
307+
require.NoError(t, err)
308+
}
309+
310+
jsonOutput := readDoc.(func() string)()
311+
312+
var jsonDoc interface{}
313+
if err := json.Unmarshal([]byte(jsonOutput), &jsonDoc); err != nil {
314+
require.NoError(t, err)
315+
}
316+
317+
expectedJSON, err := os.ReadFile(filepath.Join(config.SearchDir, "expected.json"))
318+
if err != nil {
319+
require.NoError(t, err)
320+
}
321+
322+
assert.JSONEq(t, string(expectedJSON), jsonOutput)
323+
}
324+
264325
func TestGen_jsonIndent(t *testing.T) {
265326
config := &Config{
266327
SearchDir: searchDir,

spec.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ type Spec struct {
1717
Description string
1818
InfoInstanceName string
1919
SwaggerTemplate string
20+
LeftDelim string
21+
RightDelim string
2022
}
2123

2224
// ReadDoc parses SwaggerTemplate into swagger document.
2325
func (i *Spec) ReadDoc() string {
2426
i.Description = strings.ReplaceAll(i.Description, "\n", "\\n")
2527

26-
tpl, err := template.New("swagger_info").Funcs(template.FuncMap{
28+
tpl := template.New("swagger_info").Funcs(template.FuncMap{
2729
"marshal": func(v interface{}) string {
2830
a, _ := json.Marshal(v)
2931

@@ -37,13 +39,19 @@ func (i *Spec) ReadDoc() string {
3739

3840
return strings.ReplaceAll(str, "\\\\\"", "\\\\\\\"")
3941
},
40-
}).Parse(i.SwaggerTemplate)
42+
})
43+
44+
if i.LeftDelim != "" && i.RightDelim != "" {
45+
tpl = tpl.Delims(i.LeftDelim, i.RightDelim)
46+
}
47+
48+
parsed, err := tpl.Parse(i.SwaggerTemplate)
4149
if err != nil {
4250
return i.SwaggerTemplate
4351
}
4452

4553
var doc bytes.Buffer
46-
if err = tpl.Execute(&doc, i); err != nil {
54+
if err = parsed.Execute(&doc, i); err != nil {
4755
return i.SwaggerTemplate
4856
}
4957

spec_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ func TestSpec_ReadDoc(t *testing.T) {
6363
Description string
6464
InfoInstanceName string
6565
SwaggerTemplate string
66+
LeftDelim string
67+
RightDelim string
6668
}
6769

6870
tests := []struct {
@@ -132,6 +134,37 @@ func TestSpec_ReadDoc(t *testing.T) {
132134
},
133135
want: "{{ .Schemesa }}",
134136
},
137+
{
138+
name: "TestReadDocCustomDelims",
139+
fields: fields{
140+
Version: "1.0",
141+
Host: "localhost:8080",
142+
BasePath: "/",
143+
InfoInstanceName: "TestInstanceName",
144+
SwaggerTemplate: `{
145+
"swagger": "2.0",
146+
"info": {
147+
"description": "{%escape .Description%}",
148+
"title": "{%.Title%}",
149+
"version": "{%.Version%}"
150+
},
151+
"host": "{%.Host%}",
152+
"basePath": "{%.BasePath%}",
153+
}`,
154+
LeftDelim: "{%",
155+
RightDelim: "%}",
156+
},
157+
want: "{" +
158+
"\n\t\t\t\"swagger\": \"2.0\"," +
159+
"\n\t\t\t\"info\": {" +
160+
"\n\t\t\t\t\"description\": \"\",\n\t\t\t\t\"" +
161+
"title\": \"\"," +
162+
"\n\t\t\t\t\"version\": \"1.0\"" +
163+
"\n\t\t\t}," +
164+
"\n\t\t\t\"host\": \"localhost:8080\"," +
165+
"\n\t\t\t\"basePath\": \"/\"," +
166+
"\n\t\t}",
167+
},
135168
}
136169

137170
for _, tt := range tests {
@@ -145,6 +178,8 @@ func TestSpec_ReadDoc(t *testing.T) {
145178
Description: tt.fields.Description,
146179
InfoInstanceName: tt.fields.InfoInstanceName,
147180
SwaggerTemplate: tt.fields.SwaggerTemplate,
181+
LeftDelim: tt.fields.LeftDelim,
182+
RightDelim: tt.fields.RightDelim,
148183
}
149184

150185
assert.Equal(t, tt.want, doc.ReadDoc())

testdata/delims/api/api.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package api
2+
3+
// MyFunc godoc
4+
// @Description My Function
5+
// @Success 200 {object} MyStruct
6+
// @Router /myfunc [get]
7+
func MyFunc() {}
8+
9+
type MyStruct struct {
10+
URLTemplate string `json:"urltemplate" example:"http://example.org/{{ path }}" swaggertype:"string"`
11+
}

0 commit comments

Comments
 (0)