Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
21 changes: 21 additions & 0 deletions generate/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Config struct {
OptionalGenericType string `yaml:"optional_generic_type"`
StructReferences bool `yaml:"use_struct_references"`
Extensions bool `yaml:"use_extensions"`
Plugins Plugins `yaml:"plugins"`

// The directory of the config-file (relative to which all the other paths
// are resolved). Set by ValidateAndFillDefaults.
Expand All @@ -44,6 +45,16 @@ type Config struct {
pkgPath string
}

type Plugins struct {
FieldTags []FieldTagPluginConfig `yaml:"field_tags"`
fieldTagPlugins []FieldTagPlugin `yaml:"-"`
}

type FieldTagPluginConfig struct {
Name string `yaml:"name"`
ValueFuncPath string `yaml:"path"`
}

// A TypeBinding represents a Go type to which genqlient will bind a particular
// GraphQL type, and is documented further in the [genqlient.yaml docs].
//
Expand Down Expand Up @@ -310,6 +321,16 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error {
return err
}

fieldTagPlugins := make([]FieldTagPlugin, len(c.Plugins.FieldTags))
for i, pluginConfig := range c.Plugins.FieldTags {
loadedFieldTagFunc, err := LoadFieldTagPlugins(pluginConfig.Name, pluginConfig.ValueFuncPath)
if err != nil {
return errorf(nil, "unable to load field_tags plugin %v %v: %v", pluginConfig.Name, pluginConfig.ValueFuncPath, err)
}
fieldTagPlugins[i] = *loadedFieldTagFunc
}
c.Plugins.fieldTagPlugins = fieldTagPlugins

return nil
}

Expand Down
35 changes: 35 additions & 0 deletions generate/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package generate

import (
"fmt"
"plugin"
)

type FieldTagPlugin struct {
Name string
ValueFunc func(PluginInput) (*string, error)
}

type PluginInput struct {
GraphQLName string
Description string
}

func LoadFieldTagPlugins(name string, path string) (*FieldTagPlugin, error) {
pl, err := plugin.Open(path)
if err != nil {
return nil, err
}

funcF, err := pl.Lookup("FieldTagPlugin")
if err != nil {
return nil, err
}

castedFunc, ok := funcF.(func(PluginInput) (*string, error))
if !ok {
return nil, fmt.Errorf("expected 'func(PluginInput) (string, error)' function, got %T", funcF)
}

return &FieldTagPlugin{Name: name, ValueFunc: castedFunc}, nil
}
25 changes: 21 additions & 4 deletions generate/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,19 +390,36 @@ func (typ *goStructType) WriteDefinition(w io.Writer, g *generator) error {
fmt.Fprintf(w, "type %s struct {\n", typ.GoName)
for _, field := range typ.Fields {
writeDescription(w, field.Description)
jsonTag := `"` + field.JSONName
var tags string

jsonTag := `json:"` + field.JSONName
if field.Omitempty {
jsonTag += ",omitempty"
}
jsonTag += `"`

if field.NeedsMarshaling() {
// certain types are handled in our (Un)MarshalJSON (see below)
jsonTag = `"-"`
jsonTag = `json:"-"`
}

tags = jsonTag

pluginInput := PluginInput{field.GraphQLName, field.Description}
for _, plugin := range typ.Generator.Config.Plugins.fieldTagPlugins {
res, err := plugin.ValueFunc(pluginInput)
if err != nil {
return fmt.Errorf("error running plugin %s on field %s: %w", plugin.Name, field.GoName, err)
}
if res == nil {
continue
}
tags += fmt.Sprintf(` %s:%q`, plugin.Name, *res)
}

// Note for embedded types field.GoName is "", which produces the code
// we want!
fmt.Fprintf(w, "\t%s %s `json:%s`\n",
field.GoName, field.GoType.Reference(), jsonTag)
fmt.Fprintf(w, "\t%s %s `%s`\n", field.GoName, field.GoType.Reference(), tags)
}
fmt.Fprintf(w, "}\n")

Expand Down