Extract code generation logic into internal/api package#4418
Open
kyleconroy wants to merge 1 commit intomainfrom
Open
Extract code generation logic into internal/api package#4418kyleconroy wants to merge 1 commit intomainfrom
kyleconroy wants to merge 1 commit intomainfrom
Conversation
Introduce a small programmatic interface to sqlc's code generation,
inspired by esbuild's Build API. The api package exports three names:
func Generate(ctx, GenerateOptions) GenerateResult
type GenerateOptions struct {
Config io.Reader
Stderr io.Writer
Write bool
Diff bool
BaseDir string
EnableProcessPlugins bool
}
type GenerateResult struct {
Files map[string]string
Errors []error
}
Everything else in the package — parse, codegen, the plugin shim, the
result processor — is unexported.
Notable design choices:
* Config as io.Reader. No Dir/File fields. Callers hand sqlc whatever
bytes they want; the CLI reads from disk and library callers can
construct configs in memory.
* BaseDir does double duty: it's the directory relative paths in the
config resolve against and the prefix stripped from file paths in
parse errors and diff labels. When empty it defaults to the current
working directory.
* Process plugins are off by default. EnableProcessPlugins is a single
bool whose zero value refuses any process plugin in the config. The
CLI sets it from the `processplugins` SQLCDEBUG setting (default 1).
* Write/Diff replace separate functions. `sqlc generate` is
api.Generate{Write: true}, `sqlc compile` is neither, `sqlc diff` is
{Diff: true}. cmd.Diff is gone.
CLI: each of genCmd, checkCmd, and diffCmd reads the config bytes,
chdirs into the config's directory so relative paths resolve, and calls
api.Generate with BaseDir set to that directory. cmd.Vet and cmd.Push
keep their own helpers (parse, processQuerySets, codeGenRequest) since
they have surface beyond what api covers; both packages skip joining
their dir parameter when the path is already absolute, so configs with
absolute paths flow through both.
Endtoend tests: TestExamples and TestReplay call api.Generate directly.
A mutatedConfigBytes helper parses the test's config, optionally
applies a mutation (the managed-db context adds servers + sets
database.managed), forces version "2" so v1 configs round-trip cleanly,
and re-encodes as YAML. When mutated, the bytes are also dropped to a
temp file alongside the original so cmd.Vet (which still takes a path)
can use it. Per-test environment variables from exec.json are applied
via t.Setenv, and cmd.Env is then populated via opts.ExperimentFromEnv
— same path the CLI takes.
config.AnalyzerDatabase gained MarshalYAML/MarshalJSON so the parsed
Config round-trips through yaml.Marshal cleanly — needed for the test
helper.
8700f30 to
8dcf3bb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce
internal/api, a small programmatic interface to sqlc's code generation, inspired by esbuild's Build API. One entry point,api.Generate(ctx, api.GenerateOptions{}), returns aGenerateResult. The CLI becomes a thin wrapper.Public surface
The package exports exactly three names:
Everything else in the package — parse, codegen, the plugin shim, the result processor — is unexported.
Notable design choices
io.Reader. NoDir/Filefields. Callers hand sqlc whatever bytes they want. The CLI reads from disk; library callers can construct configs in memory.BaseDirdoes double duty. It's the directory relative paths in the config resolve against and the prefix stripped from file paths in parse errors and diff labels. When empty it defaults to the current working directory. Six fields total, no separate "dir for resolving" vs. "dir for labels."EnableProcessPluginsis a single bool whose zero value refuses any process plugin in the config — process plugins execute arbitrary local commands, so callers must opt in. The CLI sets it fromenv.Debug.ProcessPlugins, which defaults to true and flips off underSQLCDEBUG=processplugins=0.Write/Diffreplace separate functions.sqlc generateisapi.Generate{Write: true},sqlc compileis neither,sqlc diffis{Diff: true}.cmd.Diffis gone.CLI
internal/cmd/cmd.go'sgenCmd,checkCmd, anddiffCmdeach:os.Chdirinto the config's directory so relative paths resolve.api.GeneratewithConfig: bytes.NewReader(data),BaseDir: configDir, andEnableProcessPlugins: env.Debug.ProcessPlugins.cmd.Vetandcmd.Pushstill use the cmd-local helpers (parse,processQuerySets,codeGenRequest) since they have surface beyond what api covers; both packages skip joining theirdirparameter when the path is already absolute, so configs with absolute paths flow through both.Endtoend tests
internal/endtoend/endtoend_test.gocallsapi.Generatedirectly. A smallmutatedConfigByteshelper parses the test's config, optionally applies a mutation (the managed-db context adds servers + setsdatabase.managed), forces version"2"so v1 configs round-trip cleanly, and re-encodes as YAML. When mutated, the bytes are also dropped to a temp file alongside the original socmd.Vet(which still takes a path) can use it.Per-test environment variables from
exec.jsonare applied viat.Setenv, andcmd.Envis then populated viaopts.DebugFromEnv/ExperimentFromEnv— same path the CLI takes.config.AnalyzerDatabasegainedMarshalYAML/MarshalJSONso the parsedConfiground-trips throughyaml.Marshalcleanly — needed for the test helper.Files
internal/api/{api,generate,process,parse,codegen,shim,diff}.gointernal/cmd/{cmd,generate,vet,process}.go,internal/config/config.go,internal/endtoend/endtoend_test.go,internal/endtoend/vet_test.gointernal/cmd/diff.goTest plan
go test ./internal/endtoend/...(TestExamples,TestReplaybase + managed-db) passes locallygo test ./...passes outside of pre-existing MySQL infra failures (TestExpandMySQL,TestValidSchema/*/mysql/*,TestExamplesVet/{authors,booktest,ondeck})go build ./...andgo vet ./...clean