Add support for interfaces, part 2: list-of-interface (#54)

## Summary:
In this commit I remove one of the limitations of our support for
interfaces, from #52, by adding support for list-of-interface fields.
This was surprisingly complex!  The issue is that, as before, it's the
containing type that has to do all the glue work -- and it's that glue
work that is complicated by list-of-interface fields.

All in all, it's not that much new code, and by far the hard part is
just 20 lines in the UnmarshalJSON template (which come with almost
twice as many lines of comments to explain them).  It may be easiest to
start by reading some of the generated code, and then read the template.

I also added support for such fields with `pointer: true` specified,
such that the type is `[][]...[]*MyInterface`, although I don't know why
you would want that.  This does *not* allow e.g. `*[]*[][]*MyInterface`;
that would require a way to specify it (see #16) but also add some extra
complexity (as we'd have to actually walk the type-unwrap chain
properly, instead of just counting the number of slices and whether
there's a pointer).

Issue: https://github.com/Khan/genqlient/issues/8

## Test plan:
make check


Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, aberkan, csilvers, MiguelCastillo

Required Reviewers: 

Approved by: dnerdy

Checks:  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Test (1.13),  Lint,  Lint,  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Test (1.13)

Pull request URL: https://github.com/Khan/genqlient/pull/54
This commit is contained in:
Ben Kraft
2021-08-25 11:57:24 -07:00
committed by GitHub
parent 4c38cb7759
commit 1e87553788
22 changed files with 1310 additions and 77 deletions
@@ -19,7 +19,40 @@ type InterfaceNestingResponse struct {
type InterfaceNestingRootTopic struct {
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenContent `json:"children"`
Children []InterfaceNestingRootTopicChildrenContent `json:"-"`
}
func (v *InterfaceNestingRootTopic) UnmarshalJSON(b []byte) error {
type InterfaceNestingRootTopicWrapper InterfaceNestingRootTopic
var firstPass struct {
*InterfaceNestingRootTopicWrapper
Children []json.RawMessage `json:"children"`
}
firstPass.InterfaceNestingRootTopicWrapper = (*InterfaceNestingRootTopicWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
{
target := &v.Children
raw := firstPass.Children
*target = make(
[]InterfaceNestingRootTopicChildrenContent,
len(raw))
for i, raw := range raw {
target := &(*target)[i]
err = __unmarshalInterfaceNestingRootTopicChildrenContent(
target, raw)
if err != nil {
return err
}
}
}
return nil
}
// InterfaceNestingRootTopicChildrenArticle includes the requested fields of the GraphQL type Article.
@@ -35,7 +68,40 @@ type InterfaceNestingRootTopicChildrenArticleParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent `json:"children"`
Children []InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent `json:"-"`
}
func (v *InterfaceNestingRootTopicChildrenArticleParentTopic) UnmarshalJSON(b []byte) error {
type InterfaceNestingRootTopicChildrenArticleParentTopicWrapper InterfaceNestingRootTopicChildrenArticleParentTopic
var firstPass struct {
*InterfaceNestingRootTopicChildrenArticleParentTopicWrapper
Children []json.RawMessage `json:"children"`
}
firstPass.InterfaceNestingRootTopicChildrenArticleParentTopicWrapper = (*InterfaceNestingRootTopicChildrenArticleParentTopicWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
{
target := &v.Children
raw := firstPass.Children
*target = make(
[]InterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent,
len(raw))
for i, raw := range raw {
target := &(*target)[i]
err = __unmarshalInterfaceNestingRootTopicChildrenArticleParentTopicChildrenContent(
target, raw)
if err != nil {
return err
}
}
}
return nil
}
// InterfaceNestingRootTopicChildrenArticleParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
@@ -160,7 +226,40 @@ type InterfaceNestingRootTopicChildrenTopicParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent `json:"children"`
Children []InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent `json:"-"`
}
func (v *InterfaceNestingRootTopicChildrenTopicParentTopic) UnmarshalJSON(b []byte) error {
type InterfaceNestingRootTopicChildrenTopicParentTopicWrapper InterfaceNestingRootTopicChildrenTopicParentTopic
var firstPass struct {
*InterfaceNestingRootTopicChildrenTopicParentTopicWrapper
Children []json.RawMessage `json:"children"`
}
firstPass.InterfaceNestingRootTopicChildrenTopicParentTopicWrapper = (*InterfaceNestingRootTopicChildrenTopicParentTopicWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
{
target := &v.Children
raw := firstPass.Children
*target = make(
[]InterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent,
len(raw))
for i, raw := range raw {
target := &(*target)[i]
err = __unmarshalInterfaceNestingRootTopicChildrenTopicParentTopicChildrenContent(
target, raw)
if err != nil {
return err
}
}
}
return nil
}
// InterfaceNestingRootTopicChildrenTopicParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.
@@ -241,7 +340,40 @@ type InterfaceNestingRootTopicChildrenVideoParentTopic struct {
Typename string `json:"__typename"`
// ID is documented in the Content interface.
Id testutil.ID `json:"id"`
Children []InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent `json:"children"`
Children []InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent `json:"-"`
}
func (v *InterfaceNestingRootTopicChildrenVideoParentTopic) UnmarshalJSON(b []byte) error {
type InterfaceNestingRootTopicChildrenVideoParentTopicWrapper InterfaceNestingRootTopicChildrenVideoParentTopic
var firstPass struct {
*InterfaceNestingRootTopicChildrenVideoParentTopicWrapper
Children []json.RawMessage `json:"children"`
}
firstPass.InterfaceNestingRootTopicChildrenVideoParentTopicWrapper = (*InterfaceNestingRootTopicChildrenVideoParentTopicWrapper)(v)
err := json.Unmarshal(b, &firstPass)
if err != nil {
return err
}
{
target := &v.Children
raw := firstPass.Children
*target = make(
[]InterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent,
len(raw))
for i, raw := range raw {
target := &(*target)[i]
err = __unmarshalInterfaceNestingRootTopicChildrenVideoParentTopicChildrenContent(
target, raw)
if err != nil {
return err
}
}
}
return nil
}
// InterfaceNestingRootTopicChildrenVideoParentTopicChildrenArticle includes the requested fields of the GraphQL type Article.