package codegen
import (
"strconv"
"strings"
"github.com/vektah/gqlparser/ast"
)
type NamedTypes map[string]*NamedType
type NamedType struct {
Ref
IsScalar bool
IsInterface bool
IsInput bool
GQLType string // Name of the graphql type
Marshaler *Ref // If this type has an external marshaler this will be set
}
type Ref struct {
GoType string // Name of the go type
Package string // the package the go type lives in
Import *Import // the resolved import with alias
IsUserDefined bool // does the type exist in the typemap
}
type Type struct {
*NamedType
Modifiers []string
ASTType *ast.Type
AliasedType *Ref
}
const (
modList = "[]"
modPtr = "*"
)
func (t Ref) FullName() string {
return t.PkgDot() + t.GoType
}
func (t Ref) PkgDot() string {
if t.Import == nil || t.Import.Alias() == "" {
return ""
}
return t.Import.Alias() + "."
}
func (t Type) Signature() string {
if t.AliasedType != nil {
return strings.Join(t.Modifiers, "") + t.AliasedType.FullName()
}
return strings.Join(t.Modifiers, "") + t.FullName()
}
func (t Type) FullSignature() string {
pkg := ""
if t.Package != "" {
pkg = t.Package + "."
}
return strings.Join(t.Modifiers, "") + pkg + t.GoType
}
func (t Type) IsPtr() bool {
return len(t.Modifiers) > 0 && t.Modifiers[0] == modPtr
}
func (t *Type) StripPtr() {
if !t.IsPtr() {
return
}
t.Modifiers = t.Modifiers[0 : len(t.Modifiers)-1]
}
func (t Type) IsSlice() bool {
return len(t.Modifiers) > 0 && t.Modifiers[0] == modList ||
len(t.Modifiers) > 1 && t.Modifiers[0] == modPtr && t.Modifiers[1] == modList
}
func (t NamedType) IsMarshaled() bool {
return t.Marshaler != nil
}
func (t Type) Unmarshal(result, raw string) string {
return t.unmarshal(result, raw, t.Modifiers, 1)
}
func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) string {
switch {
case len(remainingMods) > 0 && remainingMods[0] == modPtr:
ptr := "ptr" + strconv.Itoa(depth)
return tpl(`var {{.ptr}} {{.mods}}{{.t.FullName}}
if {{.raw}} != nil {
{{.next}}
{{.result}} = &{{.ptr -}}
}
`, map[string]interface{}{
"ptr": ptr,
"t": t,
"raw": raw,
"result": result,
"mods": strings.Join(remainingMods[1:], ""),
"next": t.unmarshal(ptr, raw, remainingMods[1:], depth+1),
})
case len(remainingMods) > 0 && remainingMods[0] == modList:
var rawIf = "rawIf" + strconv.Itoa(depth)
var index = "idx" + strconv.Itoa(depth)
return tpl(`var {{.rawSlice}} []interface{}
if {{.raw}} != nil {
if tmp1, ok := {{.raw}}.([]interface{}); ok {
{{.rawSlice}} = tmp1
} else {
{{.rawSlice}} = []interface{}{ {{.raw}} }
}
}
{{.result}} = make({{.type}}, len({{.rawSlice}}))
for {{.index}} := range {{.rawSlice}} {
{{ .next -}}
}`, map[string]interface{}{
"raw": raw,
"rawSlice": rawIf,
"index": index,
"result": result,
"type": strings.Join(remainingMods, "") + t.NamedType.FullName(),
"next": t.unmarshal(result+"["+index+"]", rawIf+"["+index+"]", remainingMods[1:], depth+1),
})
}
realResult := result
if t.AliasedType != nil {
result = "castTmp"
}
return tpl(`{{- if .t.AliasedType }}
var castTmp {{.t.FullName}}
{{ end }}
{{- if eq .t.GoType "map[string]interface{}" }}
{{- .result }} = {{.raw}}.(map[string]interface{})
{{- else if .t.Marshaler }}
{{- .result }}, err = {{ .t.Marshaler.PkgDot }}Unmarshal{{.t.Marshaler.GoType}}({{.raw}})
{{- else -}}
err = (&{{.result}}).UnmarshalGQL({{.raw}})
{{- end }}
{{- if .t.AliasedType }}
{{ .realResult }} = {{.t.AliasedType.FullName}}(castTmp)
{{- end }}`, map[string]interface{}{
"realResult": realResult,
"result": result,
"raw": raw,
"t": t,
})
}
func (t Type) Marshal(val string) string {
if t.AliasedType != nil {
val = t.GoType + "(" + val + ")"
}
if t.Marshaler != nil {
return "return " + t.Marshaler.PkgDot() + "Marshal" + t.Marshaler.GoType + "(" + val + ")"
}
return "return " + val
}