feat: add SetGlobalStateOptions and ResetGlobalState#2393
Conversation
Add SetGlobalStateOptions to allow setting globalState.options without calling Generate, following the pattern established by SetGlobalStateSpec (PR oapi-codegen#1174). Add ResetGlobalState to reset all global state fields to their zero values, preventing state leakage between successive Generate calls when using oapi-codegen as a library. Fixes oapi-codegen#1805
Greptile SummaryThis PR adds APIs for controlling codegen's package-level state. The main changes are:
Confidence Score: 3/5This is close, but the reset behavior should be fixed before merging.
|
| Filename | Overview |
|---|---|
| pkg/codegen/codegen.go | Adds global state helper APIs, but the reset helper misses option-derived package globals outside globalState. |
Reviews (1): Last reviewed commit: "feat: add SetGlobalStateOptions and Rese..." | Re-trigger Greptile
| func ResetGlobalState() { | ||
| globalState.options = Configuration{} | ||
| globalState.spec = nil | ||
| globalState.importMapping = nil | ||
| globalState.initialismsMap = nil | ||
| globalState.typeMapping = TypeMapping{} | ||
| globalState.resolvedNames = nil | ||
| globalState.resolvedClientWrapperNames = nil | ||
| globalState.streamingContentTypeRegexes = nil | ||
| } |
There was a problem hiding this comment.
Generate mutates the package-level responseTypeSuffix when OutputOptions.ResponseTypeSuffix is set, but this reset path does not restore it. When a library caller generates once with a custom suffix, calls ResetGlobalState(), and then generates again with default options, the second output still uses the old suffix because Generate only writes responseTypeSuffix for non-empty overrides. That leaves types like GetPetResult in output that should have gone back to GetPetResponse.
| func ResetGlobalState() { | |
| globalState.options = Configuration{} | |
| globalState.spec = nil | |
| globalState.importMapping = nil | |
| globalState.initialismsMap = nil | |
| globalState.typeMapping = TypeMapping{} | |
| globalState.resolvedNames = nil | |
| globalState.resolvedClientWrapperNames = nil | |
| globalState.streamingContentTypeRegexes = nil | |
| } | |
| func ResetGlobalState() { | |
| globalState.options = Configuration{} | |
| globalState.spec = nil | |
| globalState.importMapping = nil | |
| globalState.initialismsMap = nil | |
| globalState.typeMapping = TypeMapping{} | |
| globalState.resolvedNames = nil | |
| globalState.resolvedClientWrapperNames = nil | |
| globalState.streamingContentTypeRegexes = nil | |
| responseTypeSuffix = "Response" | |
| } |
| func ResetGlobalState() { | ||
| globalState.options = Configuration{} | ||
| globalState.spec = nil | ||
| globalState.importMapping = nil | ||
| globalState.initialismsMap = nil | ||
| globalState.typeMapping = TypeMapping{} | ||
| globalState.resolvedNames = nil | ||
| globalState.resolvedClientWrapperNames = nil | ||
| globalState.streamingContentTypeRegexes = nil | ||
| } |
There was a problem hiding this comment.
makeInitialismsMap rebuilds the package-level targetWordRegex with any configured AdditionalInitialisms, but ResetGlobalState() only clears the map stored inside globalState. After one generation adds an initialism such as FOO, a reset still leaves ToCamelCaseWithInitialism and paths like media type name generation using a regex that uppercases FOO. A later default generation can therefore still get names influenced by the previous options.
…sistent The oapi-codegen package we use to generate Go code keeps a bunch of internal global state that affects how code is generated. In particular, the name normalizer it uses to form types names is a global variable initialized when we call `codegen.Generate`. We call some of the codegen package's utility functions to form custom names for types before generating code. These utility functions also use the global state set up by `codegen.Generate`, which isn't initialized when we call them, resulting in the utility functions producing different names than we expect. This makes our code generation non-deterministic: we get different name capitalization depending what order schemas get generated in. Make a no-op call to `codegen.Generate` with our desired options before calling the mutators to ensure that the global state is initialized for all our calls. Note that once oapi-codegen/oapi-codegen#2393 is merged in the upstream code generator, we can use the new `SetGlobalStateOptions` function rather than the dummy `Generate` call. Fixes #113 Signed-off-by: Adam Wolfe Gordon <awg@upbound.io>
Problem
When using oapi-codegen as a library and calling
Generatemultiple times in a single process (e.g., to split client operations by tags into separate files), theglobalState.optionsfrom the first call persists and affects subsequent calls. This makes it impossible to generate separate files with different configurations.Solution
This PR adds two new exported functions, following the pattern established by
SetGlobalStateSpec(PR #1174):SetGlobalStateOptions(opts Configuration)— Allows settingglobalState.optionswithout callingGenerate. Useful when you need to override options between successiveGeneratecalls.ResetGlobalState()— Resets all global state fields to their zero values. This is a more thorough reset that prevents any state leakage between calls, including options, spec, import mappings, type mappings, initialisms, resolved names, and streaming content type regexes.Usage
Or for just overriding options:
Fixes #1805