Skip to content
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

Add "generic" option to the "optional" configuration for handling nullable types #252

Merged
merged 5 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions generate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ type Config struct {
// The following fields are documented in the [genqlient.yaml docs].
//
// [genqlient.yaml docs]: https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
Schema StringList `yaml:"schema"`
Operations StringList `yaml:"operations"`
Generated string `yaml:"generated"`
Package string `yaml:"package"`
ExportOperations string `yaml:"export_operations"`
ContextType string `yaml:"context_type"`
ClientGetter string `yaml:"client_getter"`
Bindings map[string]*TypeBinding `yaml:"bindings"`
PackageBindings []*PackageBinding `yaml:"package_bindings"`
Optional string `yaml:"optional"`
StructReferences bool `yaml:"use_struct_references"`
Extensions bool `yaml:"use_extensions"`
Schema StringList `yaml:"schema"`
Operations StringList `yaml:"operations"`
Generated string `yaml:"generated"`
Package string `yaml:"package"`
ExportOperations string `yaml:"export_operations"`
ContextType string `yaml:"context_type"`
ClientGetter string `yaml:"client_getter"`
Bindings map[string]*TypeBinding `yaml:"bindings"`
PackageBindings []*PackageBinding `yaml:"package_bindings"`
Optional string `yaml:"optional"`
OptionalGenericType string `yaml:"optional_generic_type"`
StructReferences bool `yaml:"use_struct_references"`
Extensions bool `yaml:"use_extensions"`

// Set to true to use features that aren't fully ready to use.
//
Expand Down
8 changes: 8 additions & 0 deletions generate/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package generate

import (
"fmt"
"regexp"
"sort"

"github.com/vektah/gqlparser/v2/ast"
Expand Down Expand Up @@ -262,6 +263,13 @@ func (g *generator) convertType(
// options work, recursing here isn't as connvenient.)
// Note this does []*T or [][]*T, not e.g. *[][]T. See #16.
goTyp = &goPointerType{goTyp}
} else if !typ.NonNull && g.Config.Optional == "generic" {
qualifiedTypeName := regexp.MustCompile("[^/]+$").FindString(g.Config.OptionalGenericType)

DylanRJohnston marked this conversation as resolved.
Show resolved Hide resolved
goTyp = &goGenericType{
GenericName: qualifiedTypeName,
Elem: goTyp,
}
}
return goTyp, err
}
Expand Down
7 changes: 7 additions & 0 deletions generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,13 @@ func Generate(config *Config) (map[string][]byte, error) {
}
}

if g.Config.Optional == "generic" {
_, err = g.ref(g.Config.OptionalGenericType)
if err != nil {
return nil, err
}
}
DylanRJohnston marked this conversation as resolved.
Show resolved Hide resolved

// Now really glue it all together, and format.
var buf bytes.Buffer
err = g.render("header.go.tmpl", &buf, g)
Expand Down
16 changes: 16 additions & 0 deletions generate/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (
_ goType = (*goEnumType)(nil)
_ goType = (*goStructType)(nil)
_ goType = (*goInterfaceType)(nil)
_ goType = (*goGenericType)(nil)
)

type (
Expand All @@ -80,6 +81,12 @@ type (
// user (perhaps to handle nulls explicitly, or to avoid copying large
// structures).
goPointerType struct{ Elem goType }
// goGenericType represent the Go type ELem, used when requested by the
DylanRJohnston marked this conversation as resolved.
Show resolved Hide resolved
// user (to box null explicitly)
goGenericType struct {
GenericName string
Elem goType
}
)

// Opaque types are defined by the user; pointers and slices need no definition
Expand All @@ -91,21 +98,27 @@ func (typ *goTypenameForBuiltinType) WriteDefinition(w io.Writer, g *generator)
}
func (typ *goSliceType) WriteDefinition(io.Writer, *generator) error { return nil }
func (typ *goPointerType) WriteDefinition(io.Writer, *generator) error { return nil }
func (typ *goGenericType) WriteDefinition(io.Writer, *generator) error { return nil }

func (typ *goOpaqueType) Reference() string { return typ.GoRef }
func (typ *goTypenameForBuiltinType) Reference() string { return typ.GoTypeName }
func (typ *goSliceType) Reference() string { return "[]" + typ.Elem.Reference() }
func (typ *goPointerType) Reference() string { return "*" + typ.Elem.Reference() }
func (typ *goGenericType) Reference() string {
return strings.Replace(typ.GenericName, "%", typ.Elem.Reference(), 1)
}

func (typ *goOpaqueType) SelectionSet() ast.SelectionSet { return nil }
func (typ *goTypenameForBuiltinType) SelectionSet() ast.SelectionSet { return nil }
func (typ *goSliceType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() }
func (typ *goPointerType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() }
func (typ *goGenericType) SelectionSet() ast.SelectionSet { return typ.Elem.SelectionSet() }

func (typ *goOpaqueType) GraphQLTypeName() string { return typ.GraphQLName }
func (typ *goTypenameForBuiltinType) GraphQLTypeName() string { return typ.GraphQLName }
func (typ *goSliceType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() }
func (typ *goPointerType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() }
func (typ *goGenericType) GraphQLTypeName() string { return typ.Elem.GraphQLTypeName() }

// goEnumType represents a Go named-string type used to represent a GraphQL
// enum. In this case, we generate both the type (`type T string`) and also a
Expand Down Expand Up @@ -529,6 +542,7 @@ func (typ *goOpaqueType) Unwrap() goType { return typ }
func (typ *goTypenameForBuiltinType) Unwrap() goType { return typ }
func (typ *goSliceType) Unwrap() goType { return typ.Elem.Unwrap() }
func (typ *goPointerType) Unwrap() goType { return typ.Elem.Unwrap() }
func (typ *goGenericType) Unwrap() goType { return typ.Elem.Unwrap() }
func (typ *goEnumType) Unwrap() goType { return typ }
func (typ *goStructType) Unwrap() goType { return typ }
func (typ *goInterfaceType) Unwrap() goType { return typ }
Expand All @@ -537,6 +551,7 @@ func (typ *goOpaqueType) SliceDepth() int { return 0 }
func (typ *goTypenameForBuiltinType) SliceDepth() int { return 0 }
func (typ *goSliceType) SliceDepth() int { return typ.Elem.SliceDepth() + 1 }
func (typ *goPointerType) SliceDepth() int { return 0 }
func (typ *goGenericType) SliceDepth() int { return 0 }
func (typ *goEnumType) SliceDepth() int { return 0 }
func (typ *goStructType) SliceDepth() int { return 0 }
func (typ *goInterfaceType) SliceDepth() int { return 0 }
Expand All @@ -545,6 +560,7 @@ func (typ *goOpaqueType) IsPointer() bool { return false }
func (typ *goTypenameForBuiltinType) IsPointer() bool { return false }
func (typ *goSliceType) IsPointer() bool { return typ.Elem.IsPointer() }
func (typ *goPointerType) IsPointer() bool { return true }
func (typ *goGenericType) IsPointer() bool { return false }
func (typ *goEnumType) IsPointer() bool { return false }
func (typ *goStructType) IsPointer() bool { return false }
func (typ *goInterfaceType) IsPointer() bool { return false }
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ go 1.16

require (
github.com/99designs/gqlgen v0.17.2
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alexflint/go-arg v1.4.2
github.com/bradleyjkemp/cupaloy/v2 v2.6.0
github.com/stretchr/testify v1.7.0
github.com/vektah/gqlparser/v2 v2.4.5
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
golang.org/x/tools v0.1.10
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/agnivade/levenshtein v1.1.1 // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
)