_examples/scalars/model/model.go (158 lines of code) (raw):
package model
import (
"errors"
"fmt"
"io"
"strconv"
"strings"
"time"
"github.com/99designs/gqlgen/_examples/scalars/external"
"github.com/99designs/gqlgen/graphql"
)
type Banned bool
func (b Banned) MarshalGQL(w io.Writer) {
if b {
w.Write([]byte("true"))
} else {
w.Write([]byte("false"))
}
}
func (b *Banned) UnmarshalGQL(v any) error {
switch v := v.(type) {
case string:
*b = Banned(strings.EqualFold(v, "true"))
return nil
case bool:
*b = Banned(v)
return nil
default:
return fmt.Errorf("%T is not a bool", v)
}
}
type User struct {
ID external.ObjectID
Name string
Created time.Time // direct binding to builtin types with external Marshal/Unmarshal methods
Modified *time.Time // direct binding to builtin types with external Marshal/Unmarshal methods
ValPrefs Prefs // external un/marshal that act on pointers
PtrPrefs *Prefs
IsBanned Banned
Address Address
Tier Tier
}
// Point is serialized as a simple array, eg [1, 2]
type Point struct {
X int
Y int
}
func (p *Point) UnmarshalGQL(v any) error {
pointStr, ok := v.(string)
if !ok {
return errors.New("points must be strings")
}
parts := strings.Split(pointStr, ",")
if len(parts) != 2 {
return errors.New("points must have 2 parts")
}
var err error
if p.X, err = strconv.Atoi(parts[0]); err != nil {
return err
}
if p.Y, err = strconv.Atoi(parts[1]); err != nil {
return err
}
return nil
}
// MarshalGQL implements the graphql.Marshaler interface
func (p Point) MarshalGQL(w io.Writer) {
fmt.Fprintf(w, `"%d,%d"`, p.X, p.Y)
}
// if the type referenced in .gqlgen.yml is a function that returns a marshaller we can use it to encode and decode
// onto any existing go type.
func MarshalTimestamp(t time.Time) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
io.WriteString(w, strconv.FormatInt(t.Unix(), 10))
})
}
// Unmarshal{Typename} is only required if the scalar appears as an input. The raw values have already been decoded
// from json into int/float64/bool/nil/map[string]interface/[]interface
func UnmarshalTimestamp(v any) (time.Time, error) {
if tmpStr, ok := v.(int64); ok {
return time.Unix(tmpStr, 0), nil
}
return time.Time{}, errors.New("time should be a unix timestamp")
}
// Lets redefine the base ID type to use an id from an external library
func MarshalID(id external.ObjectID) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
io.WriteString(w, strconv.Quote(fmt.Sprintf("=%d=", id)))
})
}
// And the same for the unmarshaler
func UnmarshalID(v any) (external.ObjectID, error) {
str, ok := v.(string)
if !ok {
return 0, errors.New("ids must be strings")
}
i, err := strconv.Atoi(str[1 : len(str)-1])
return external.ObjectID(i), err
}
type SearchArgs struct {
Location *Point
CreatedAfter *time.Time
IsBanned Banned
}
// A custom enum that uses integers to represent the values in memory but serialize as string for graphql
type Tier uint
const (
TierA Tier = iota
TierB Tier = iota
TierC Tier = iota
)
func TierForStr(str string) (Tier, error) {
switch str {
case "A":
return TierA, nil
case "B":
return TierB, nil
case "C":
return TierC, nil
default:
return 0, fmt.Errorf("%s is not a valid Tier", str)
}
}
func (e Tier) IsValid() bool {
switch e {
case TierA, TierB, TierC:
return true
}
return false
}
func (e Tier) String() string {
switch e {
case TierA:
return "A"
case TierB:
return "B"
case TierC:
return "C"
default:
panic("invalid enum value")
}
}
func (e *Tier) UnmarshalGQL(v any) error {
str, ok := v.(string)
if !ok {
return errors.New("enums must be strings")
}
var err error
*e, err = TierForStr(str)
return err
}
func (e Tier) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type Prefs struct {
DarkMode bool
}
func MarshalPreferences(p *Prefs) graphql.Marshaler {
return graphql.MarshalBoolean(p.DarkMode)
}
func UnmarshalPreferences(v any) (*Prefs, error) {
tmp, err := graphql.UnmarshalBoolean(v)
if err != nil {
return nil, err
}
return &Prefs{DarkMode: tmp}, nil
}