Skip to content

Commit 6e66df9

Browse files
author
marcinromaszewicz
committed
Rewrite configuration options
The way configuration was handled was becoming a mess. I've rewritten it to expose the configuration struct from the codegen package directly. This is a very breaking change, so the `--old-config-style` flag will use the old configuration parsing, and `--output-config` will output the new style config file from old config to aid in rewriting.
1 parent 9c4d551 commit 6e66df9

File tree

35 files changed

+458
-267
lines changed

35 files changed

+458
-267
lines changed

cmd/oapi-codegen/oapi-codegen.go

Lines changed: 181 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"io/ioutil"
2020
"os"
2121
"path"
22-
"path/filepath"
2322
"runtime/debug"
2423
"strings"
2524

@@ -35,56 +34,67 @@ func errExit(format string, args ...interface{}) {
3534
}
3635

3736
var (
38-
flagPackageName string
37+
flagOutputFile string
38+
flagConfigFile string
39+
flagOldConfigStyle bool
40+
flagOutputConfig bool
41+
flagPrintVersion bool
42+
flagPackageName string
43+
44+
// The options below are deprecated, and they will be removed in a future
45+
// release. Please use the new config file format.
3946
flagGenerate string
40-
flagOutputFile string
4147
flagIncludeTags string
4248
flagExcludeTags string
4349
flagTemplatesDir string
4450
flagImportMapping string
4551
flagExcludeSchemas string
46-
flagConfigFile string
4752
flagResponseTypeSuffix string
4853
flagAliasTypes bool
49-
flagPrintVersion bool
50-
flagOldAllOfOutput bool
51-
flagOldEnumConflicts bool
52-
flagOldAliasing bool
5354
)
5455

5556
type configuration struct {
56-
PackageName string `yaml:"package"`
57-
GenerateTargets []string `yaml:"generate"`
58-
OutputFile string `yaml:"output"`
59-
IncludeTags []string `yaml:"include-tags"`
60-
ExcludeTags []string `yaml:"exclude-tags"`
61-
TemplatesDir string `yaml:"templates"`
62-
ImportMapping map[string]string `yaml:"import-mapping"`
63-
ExcludeSchemas []string `yaml:"exclude-schemas"`
64-
OldAllOfOutput bool `yaml:"old-all-of-output"`
65-
OldEnumConflicts bool `yaml:"old-enum-conflicts"`
66-
OldAliasing bool `yaml:"old-aliasing"`
67-
ResponseTypeSuffix string `yaml:"response-type-suffix"`
57+
codegen.Configuration `yaml:",inline"`
58+
59+
// OutputFile is the filename to output.
60+
OutputFile string `yaml:"output,omitempty"`
6861
}
6962

70-
func main() {
63+
// This structure is deprecated. Please add no more flags here. It is here
64+
// for backwards compatibility and it will be removed in the future.
65+
type oldConfiguration struct {
66+
PackageName string `yaml:"package"`
67+
GenerateTargets []string `yaml:"generate"`
68+
OutputFile string `yaml:"output"`
69+
IncludeTags []string `yaml:"include-tags"`
70+
ExcludeTags []string `yaml:"exclude-tags"`
71+
TemplatesDir string `yaml:"templates"`
72+
ImportMapping map[string]string `yaml:"import-mapping"`
73+
ExcludeSchemas []string `yaml:"exclude-schemas"`
74+
ResponseTypeSuffix string `yaml:"response-type-suffix"`
75+
Compatibility codegen.CompatibilityOptions `yaml:"compatibility"`
76+
}
7177

78+
func main() {
79+
flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default")
80+
flag.BoolVar(&flagOldConfigStyle, "old-config-style", false, "whether to use the older style config file format")
81+
flag.BoolVar(&flagOutputConfig, "output-config", false, "when true, outputs a configuration file for oapi-codegen using current settings")
82+
flag.StringVar(&flagConfigFile, "config", "", "a YAML config file that controls oapi-codegen behavior")
83+
flag.BoolVar(&flagPrintVersion, "version", false, "when specified, print version and exit")
7284
flag.StringVar(&flagPackageName, "package", "", "The package name for generated code")
85+
86+
// All flags below are deprecated, and will be removed in a future release. Please do not
87+
// update their behavior.
7388
flag.StringVar(&flagGenerate, "generate", "types,client,server,spec",
7489
`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "spec", "skip-fmt", "skip-prune"`)
75-
flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default")
7690
flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
7791
flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
7892
flag.StringVar(&flagTemplatesDir, "templates", "", "Path to directory containing user templates")
7993
flag.StringVar(&flagImportMapping, "import-mapping", "", "A dict from the external reference to golang package path")
8094
flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation")
81-
flag.StringVar(&flagConfigFile, "config", "", "a YAML config file that controls oapi-codegen behavior")
8295
flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "the suffix used for responses types")
8396
flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible")
84-
flag.BoolVar(&flagPrintVersion, "version", false, "when specified, print version and exit")
85-
flag.BoolVar(&flagOldAllOfOutput, "old-all-of-output", false, "when true, old and incorrect AllOf implementation is used")
86-
flag.BoolVar(&flagOldEnumConflicts, "old-enum-conflicts", false, "when true, use old style enum naming")
87-
flag.BoolVar(&flagOldAliasing, "old-aliasing", false, "when true, generate older style redefined types")
97+
8898
flag.Parse()
8999

90100
if flagPrintVersion {
@@ -103,81 +113,70 @@ func main() {
103113
os.Exit(1)
104114
}
105115

106-
cfg := configFromFlags()
107-
108-
// If the package name has not been specified, we will use the name of the
109-
// swagger file.
110-
if cfg.PackageName == "" {
111-
path := flag.Arg(0)
112-
baseName := filepath.Base(path)
113-
// Split the base name on '.' to get the first part of the file.
114-
nameParts := strings.Split(baseName, ".")
115-
cfg.PackageName = codegen.ToCamelCase(nameParts[0])
116-
}
117-
118-
opts := codegen.Options{
119-
AliasTypes: flagAliasTypes,
120-
ResponseTypeSuffix: flagResponseTypeSuffix,
121-
}
122-
for _, g := range cfg.GenerateTargets {
123-
switch g {
124-
case "client":
125-
opts.GenerateClient = true
126-
case "chi-server":
127-
opts.GenerateChiServer = true
128-
case "server":
129-
opts.GenerateEchoServer = true
130-
case "gin":
131-
opts.GenerateGinServer = true
132-
case "types":
133-
opts.GenerateTypes = true
134-
case "spec":
135-
opts.EmbedSpec = true
136-
case "skip-fmt":
137-
opts.SkipFmt = true
138-
case "skip-prune":
139-
opts.SkipPrune = true
140-
default:
141-
fmt.Printf("unknown generate option %s\n", g)
142-
flag.PrintDefaults()
143-
os.Exit(1)
116+
var opts configuration
117+
if !flagOldConfigStyle {
118+
// We simply read the configuration from disk.
119+
if flagConfigFile != "" {
120+
buf, err := ioutil.ReadFile(flagConfigFile)
121+
if err != nil {
122+
errExit("error reading config file '%s': %v", flagConfigFile, err)
123+
}
124+
err = yaml.Unmarshal(buf, &opts)
125+
if err != nil {
126+
errExit("error parsing'%s' as YAML: %v", flagConfigFile, err)
127+
}
128+
}
129+
var err error
130+
opts, err = updateConfigFromFlags(opts)
131+
if err != nil {
132+
errExit("error processing flags: %v", err)
133+
}
134+
} else {
135+
var oldConfig oldConfiguration
136+
if flagConfigFile != "" {
137+
buf, err := ioutil.ReadFile(flagConfigFile)
138+
if err != nil {
139+
errExit("error reading config file '%s': %v", flagConfigFile, err)
140+
}
141+
err = yaml.Unmarshal(buf, &oldConfig)
142+
if err != nil {
143+
errExit("error parsing'%s' as YAML: %v", flagConfigFile, err)
144+
}
144145
}
146+
opts = newConfigFromOldConfig(oldConfig)
145147
}
146148

147-
opts.IncludeTags = cfg.IncludeTags
148-
opts.ExcludeTags = cfg.ExcludeTags
149-
opts.ExcludeSchemas = cfg.ExcludeSchemas
149+
// Ensure default values are set if user hasn't specified some needed
150+
// fields.
151+
opts.Configuration = opts.UpdateDefaults()
150152

151-
if opts.GenerateEchoServer && opts.GenerateChiServer {
152-
errExit("can not specify both server and chi-server targets simultaneously")
153+
// Now, ensure that the config options are valid.
154+
if err := opts.Validate(); err != nil {
155+
errExit("configuration error: %v", err)
153156
}
154157

155-
swagger, err := util.LoadSwagger(flag.Arg(0))
156-
if err != nil {
157-
errExit("error loading swagger spec in %s\n: %s", flag.Arg(0), err)
158+
// If the user asked to output configuration, output it to stdout and exit
159+
if flagOutputConfig {
160+
buf, err := yaml.Marshal(opts)
161+
if err != nil {
162+
errExit("error YAML marshaling configuration: %v", err)
163+
}
164+
fmt.Print(string(buf))
165+
return
158166
}
159167

160-
templates, err := loadTemplateOverrides(cfg.TemplatesDir)
168+
swagger, err := util.LoadSwagger(flag.Arg(0))
161169
if err != nil {
162-
errExit("error loading template overrides: %s\n", err)
170+
errExit("error loading swagger spec in %s\n: %s", flag.Arg(0), err)
163171
}
164-
opts.UserTemplates = templates
165-
166-
opts.ImportMapping = cfg.ImportMapping
167-
168-
opts.OldMergeSchemas = cfg.OldAllOfOutput
169172

170-
opts.OldEnumConflicts = cfg.OldEnumConflicts
171-
172-
opts.OldAliasing = cfg.OldAliasing
173-
174-
code, err := codegen.Generate(swagger, cfg.PackageName, opts)
173+
code, err := codegen.Generate(swagger, opts.Configuration)
175174
if err != nil {
176175
errExit("error generating code: %s\n", err)
177176
}
178177

179-
if cfg.OutputFile != "" {
180-
err = ioutil.WriteFile(cfg.OutputFile, []byte(code), 0644)
178+
if opts.OutputFile != "" {
179+
err = ioutil.WriteFile(opts.OutputFile, []byte(code), 0644)
181180
if err != nil {
182181
errExit("error writing generated code to file: %s", err)
183182
}
@@ -222,25 +221,54 @@ func loadTemplateOverrides(templatesDir string) (map[string]string, error) {
222221
return templates, nil
223222
}
224223

225-
// configFromFlags parses the flags and the config file. Anything which is
226-
// a zerovalue in the configuration file will be replaced with the flag
227-
// value, this means that the config file overrides flag values.
228-
func configFromFlags() *configuration {
229-
var cfg configuration
224+
// updateConfigFromFlags updates a loaded configuration from flags. Flags
225+
// override anything in the file. We generate errors for command line options
226+
// associated with the old style configuration
227+
func updateConfigFromFlags(cfg configuration) (configuration, error) {
228+
if flagPackageName != "" {
229+
cfg.PackageName = flagPackageName
230+
}
230231

231-
// Load the configuration file first.
232-
if flagConfigFile != "" {
233-
f, err := os.Open(flagConfigFile)
234-
if err != nil {
235-
errExit("failed to open config file with error: %v\n", err)
236-
}
237-
defer f.Close()
238-
err = yaml.NewDecoder(f).Decode(&cfg)
239-
if err != nil {
240-
errExit("error parsing config file: %v\n", err)
241-
}
232+
var unsupportedFlags []string
233+
234+
if flagGenerate != "types,client,server,spec" {
235+
unsupportedFlags = append(unsupportedFlags, "--generate")
236+
}
237+
if flagIncludeTags != "" {
238+
unsupportedFlags = append(unsupportedFlags, "--include-tags")
239+
}
240+
if flagExcludeTags != "" {
241+
unsupportedFlags = append(unsupportedFlags, "--exclude-tags")
242+
}
243+
if flagTemplatesDir != "" {
244+
unsupportedFlags = append(unsupportedFlags, "--templates")
245+
}
246+
if flagImportMapping != "" {
247+
unsupportedFlags = append(unsupportedFlags, "--import-mapping")
248+
}
249+
if flagExcludeSchemas != "" {
250+
unsupportedFlags = append(unsupportedFlags, "--exclude-schemas")
251+
}
252+
if flagResponseTypeSuffix != "" {
253+
unsupportedFlags = append(unsupportedFlags, "--response-type-suffix")
254+
}
255+
if flagAliasTypes {
256+
unsupportedFlags = append(unsupportedFlags, "--alias-types")
257+
}
258+
259+
if len(unsupportedFlags) > 0 {
260+
return configuration{}, fmt.Errorf("flags %s aren't supported in "+
261+
"new config style, please use --old-style-config or update your configuration",
262+
strings.Join(unsupportedFlags, ", "))
242263
}
243264

265+
return cfg, nil
266+
}
267+
268+
// updateOldConfigFromFlags parses the flags and the config file. Anything which is
269+
// a zerovalue in the configuration file will be replaced with the flag
270+
// value, this means that the config file overrides flag values.
271+
func updateOldConfigFromFlags(cfg oldConfiguration) oldConfiguration {
244272
if cfg.PackageName == "" {
245273
cfg.PackageName = flagPackageName
246274
}
@@ -269,18 +297,61 @@ func configFromFlags() *configuration {
269297
if cfg.OutputFile == "" {
270298
cfg.OutputFile = flagOutputFile
271299
}
300+
return cfg
301+
}
272302

273-
if cfg.OldAllOfOutput == false {
274-
cfg.OldAllOfOutput = flagOldAllOfOutput
303+
func newConfigFromOldConfig(c oldConfiguration) configuration {
304+
// Take flags into account.
305+
cfg := updateOldConfigFromFlags(c)
306+
307+
// Now, copy over field by field, translating flags and old values as
308+
// necessary.
309+
opts := codegen.Configuration{
310+
PackageName: cfg.PackageName,
275311
}
312+
opts.OutputOptions.ResponseTypeSuffix = flagResponseTypeSuffix
276313

277-
if cfg.OldEnumConflicts == false {
278-
cfg.OldEnumConflicts = flagOldEnumConflicts
314+
for _, g := range cfg.GenerateTargets {
315+
switch g {
316+
case "client":
317+
opts.Generate.Client = true
318+
case "chi-server":
319+
opts.Generate.ChiServer = true
320+
case "server":
321+
opts.Generate.EchoServer = true
322+
case "gin":
323+
opts.Generate.GinServer = true
324+
case "types":
325+
opts.Generate.Models = true
326+
case "spec":
327+
opts.Generate.EmbeddedSpec = true
328+
case "skip-fmt":
329+
opts.OutputOptions.SkipFmt = true
330+
case "skip-prune":
331+
opts.OutputOptions.SkipPrune = true
332+
default:
333+
fmt.Printf("unknown generate option %s\n", g)
334+
flag.PrintDefaults()
335+
os.Exit(1)
336+
}
279337
}
280338

281-
if cfg.OldAliasing == false {
282-
cfg.OldAliasing = flagOldAliasing
339+
opts.OutputOptions.IncludeTags = cfg.IncludeTags
340+
opts.OutputOptions.ExcludeTags = cfg.ExcludeTags
341+
opts.OutputOptions.ExcludeSchemas = cfg.ExcludeSchemas
342+
343+
templates, err := loadTemplateOverrides(cfg.TemplatesDir)
344+
if err != nil {
345+
errExit("error loading template overrides: %s\n", err)
283346
}
347+
opts.OutputOptions.UserTemplates = templates
348+
349+
opts.ImportMapping = cfg.ImportMapping
350+
351+
opts.Compatibility = cfg.Compatibility
284352

285-
return &cfg
353+
return configuration{
354+
Configuration: opts,
355+
OutputFile: cfg.OutputFile,
356+
}
286357
}
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
output:
2-
api.gen.go
31
package: api
42
generate:
5-
- types
6-
- client
7-
- server
8-
- spec
9-
- skip-prune
3+
echo-server: true
4+
client: true
5+
models: true
6+
embedded-spec: true
7+
output: api.gen.go
8+
output-options:
9+
skip-prune: true
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package: api
2+
generate:
3+
chi-server: true
4+
models: true
5+
embedded-spec: true
6+
output: petstore.gen.go

0 commit comments

Comments
 (0)