-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor import-loading to simplify the type-generation code #101
Conversation
genqlient has some code (`imports.go`) dedicated to tracking which imports we need and avoiding conflicts, as well as converting a (restricted) Go expression like `map[string]github.com/me/mypkg.MyType` to an import (`github.com/me/mypkg`) and a type-reference (`map[string]mypkg.MyType`) to be used in the context of that import, and at least making some attempt to track conflicts. (Right now the conflict-avoidance is not very smart, and not very well tested, but it comes up rarely anyway.) Sadly, that code was a bit cumbersome to use, because you had to first register the imports (typically from `convert.go`), then use them (often from the template). In this commit I refactor the order we write things in order to allow a significant simplification of how we import; in particular we no longer have to guess in advance what imports which template will need; it can just do `{{ref <expr>}}` as before, and it just works. To do this, I: - changed the importer to have only one API, which adds the import if needed, and returns the reference either way - added a check that we don't add imports after they're written - reorganized the toplevel templates a bit to make sure that check never fires; we now generate all the types and operations, then write the imports and glue it all together This removes a bunch of silly code, and should simplify the process of adding custom (un)marshalers (#38). While I was at it, I put the documentation of what expressions we support in a more visible place, and added a type-assertion that your custom context type implements context.Context (if applicable). Test plan: make check Reviewers: marksandstrom, mahtab, jvoll, adam, miguel, steve
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, thanks! The $
was kind of bugging me too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! The buffering is much easier to reason about compared to pre-registering references.
generate/generate.go
Outdated
for _, op := range document.Operations { | ||
if err = g.addOperation(op); err != nil { | ||
for _, operation := range g.Operations { | ||
err = g.execute("operation.go.tmpl", &bodyBuf, operation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know "execute" is based on text/template terminology, but that isn't super clear in the context of the generator, IMO. Perhaps this function could be named renderTemplate
or something similar.
generate/imports.go
Outdated
if !ok { | ||
if g.importsLocked { | ||
return "", errorf(nil, | ||
`genqlient internal error: imports locked but no alias defined for package "%v"`, pkgPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps: "imports locked but package "%v" has not been imported"?
Summary:
genqlient has some code (
imports.go
) dedicated to tracking whichimports we need and avoiding conflicts, as well as converting a
(restricted) Go expression like
map[string]github.com/me/mypkg.MyType
to an import (
github.com/me/mypkg
) and a type-reference(
map[string]mypkg.MyType
) to be used in the context of that import,and at least making some attempt to track conflicts. (Right now the
conflict-avoidance is not very smart, and not very well tested, but it
comes up rarely anyway.) Sadly, that code was a bit cumbersome to use,
because you had to first register the imports (typically from
convert.go
), then use them (often from the template).In this commit I refactor the order we write things in order to allow a
significant simplification of how we import; in particular we no longer
have to guess in advance what imports which template will need; it can
just do
{{ref <expr>}}
as before, and it just works. To do this, I:needed, and returns the reference either way
fires; we now generate all the types and operations, then write the
imports and glue it all together
This removes a bunch of silly code, and should simplify the process of
adding custom (un)marshalers (Allow mapping directly to a type but not using its standard JSON serialization #38).
While I was at it, I put the documentation of what expressions we
support in a more visible place, and added a type-assertion that your
custom context type implements context.Context (if applicable).
Test plan:
make check