aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/vektah/gqlgen/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/vektah/gqlgen/graphql')
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/bool.go30
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/context.go145
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/defer.go30
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/error.go46
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/exec.go118
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/float.go26
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/id.go33
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/int.go26
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/jsonw.go83
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/map.go24
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/oneshot.go14
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/recovery.go19
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/response.go18
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/string.go63
-rw-r--r--vendor/github.com/vektah/gqlgen/graphql/time.go21
15 files changed, 696 insertions, 0 deletions
diff --git a/vendor/github.com/vektah/gqlgen/graphql/bool.go b/vendor/github.com/vektah/gqlgen/graphql/bool.go
new file mode 100644
index 00000000..7053bbca
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/bool.go
@@ -0,0 +1,30 @@
+package graphql
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+func MarshalBoolean(b bool) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ if b {
+ w.Write(trueLit)
+ } else {
+ w.Write(falseLit)
+ }
+ })
+}
+
+func UnmarshalBoolean(v interface{}) (bool, error) {
+ switch v := v.(type) {
+ case string:
+ return "true" == strings.ToLower(v), nil
+ case int:
+ return v != 0, nil
+ case bool:
+ return v, nil
+ default:
+ return false, fmt.Errorf("%T is not a bool", v)
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/context.go b/vendor/github.com/vektah/gqlgen/graphql/context.go
new file mode 100644
index 00000000..8f544100
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/context.go
@@ -0,0 +1,145 @@
+package graphql
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/vektah/gqlgen/neelance/query"
+)
+
+type Resolver func(ctx context.Context) (res interface{}, err error)
+type ResolverMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error)
+type RequestMiddleware func(ctx context.Context, next func(ctx context.Context) []byte) []byte
+
+type RequestContext struct {
+ RawQuery string
+ Variables map[string]interface{}
+ Doc *query.Document
+ // ErrorPresenter will be used to generate the error
+ // message from errors given to Error().
+ ErrorPresenter ErrorPresenterFunc
+ Recover RecoverFunc
+ ResolverMiddleware ResolverMiddleware
+ RequestMiddleware RequestMiddleware
+
+ errorsMu sync.Mutex
+ Errors []*Error
+}
+
+func DefaultResolverMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) {
+ return next(ctx)
+}
+
+func DefaultRequestMiddleware(ctx context.Context, next func(ctx context.Context) []byte) []byte {
+ return next(ctx)
+}
+
+func NewRequestContext(doc *query.Document, query string, variables map[string]interface{}) *RequestContext {
+ return &RequestContext{
+ Doc: doc,
+ RawQuery: query,
+ Variables: variables,
+ ResolverMiddleware: DefaultResolverMiddleware,
+ RequestMiddleware: DefaultRequestMiddleware,
+ Recover: DefaultRecover,
+ ErrorPresenter: DefaultErrorPresenter,
+ }
+}
+
+type key string
+
+const (
+ request key = "request_context"
+ resolver key = "resolver_context"
+)
+
+func GetRequestContext(ctx context.Context) *RequestContext {
+ val := ctx.Value(request)
+ if val == nil {
+ return nil
+ }
+
+ return val.(*RequestContext)
+}
+
+func WithRequestContext(ctx context.Context, rc *RequestContext) context.Context {
+ return context.WithValue(ctx, request, rc)
+}
+
+type ResolverContext struct {
+ // The name of the type this field belongs to
+ Object string
+ // These are the args after processing, they can be mutated in middleware to change what the resolver will get.
+ Args map[string]interface{}
+ // The raw field
+ Field CollectedField
+ // The path of fields to get to this resolver
+ Path []interface{}
+}
+
+func (r *ResolverContext) PushField(alias string) {
+ r.Path = append(r.Path, alias)
+}
+
+func (r *ResolverContext) PushIndex(index int) {
+ r.Path = append(r.Path, index)
+}
+
+func (r *ResolverContext) Pop() {
+ r.Path = r.Path[0 : len(r.Path)-1]
+}
+
+func GetResolverContext(ctx context.Context) *ResolverContext {
+ val := ctx.Value(resolver)
+ if val == nil {
+ return nil
+ }
+
+ return val.(*ResolverContext)
+}
+
+func WithResolverContext(ctx context.Context, rc *ResolverContext) context.Context {
+ parent := GetResolverContext(ctx)
+ rc.Path = nil
+ if parent != nil {
+ rc.Path = append(rc.Path, parent.Path...)
+ }
+ if rc.Field.Alias != "" {
+ rc.PushField(rc.Field.Alias)
+ }
+ return context.WithValue(ctx, resolver, rc)
+}
+
+// This is just a convenient wrapper method for CollectFields
+func CollectFieldsCtx(ctx context.Context, satisfies []string) []CollectedField {
+ reqctx := GetRequestContext(ctx)
+ resctx := GetResolverContext(ctx)
+ return CollectFields(reqctx.Doc, resctx.Field.Selections, satisfies, reqctx.Variables)
+}
+
+// Errorf sends an error string to the client, passing it through the formatter.
+func (c *RequestContext) Errorf(ctx context.Context, format string, args ...interface{}) {
+ c.errorsMu.Lock()
+ defer c.errorsMu.Unlock()
+
+ c.Errors = append(c.Errors, c.ErrorPresenter(ctx, fmt.Errorf(format, args...)))
+}
+
+// Error sends an error to the client, passing it through the formatter.
+func (c *RequestContext) Error(ctx context.Context, err error) {
+ c.errorsMu.Lock()
+ defer c.errorsMu.Unlock()
+
+ c.Errors = append(c.Errors, c.ErrorPresenter(ctx, err))
+}
+
+// AddError is a convenience method for adding an error to the current response
+func AddError(ctx context.Context, err error) {
+ GetRequestContext(ctx).Error(ctx, err)
+}
+
+// AddErrorf is a convenience method for adding an error to the current response
+func AddErrorf(ctx context.Context, format string, args ...interface{}) {
+ GetRequestContext(ctx).Errorf(ctx, format, args...)
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/defer.go b/vendor/github.com/vektah/gqlgen/graphql/defer.go
new file mode 100644
index 00000000..79346a84
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/defer.go
@@ -0,0 +1,30 @@
+package graphql
+
+import (
+ "io"
+ "sync"
+)
+
+// Defer will begin executing the given function and immediately return a result that will block until the function completes
+func Defer(f func() Marshaler) Marshaler {
+ var deferred deferred
+ deferred.mu.Lock()
+
+ go func() {
+ deferred.result = f()
+ deferred.mu.Unlock()
+ }()
+
+ return &deferred
+}
+
+type deferred struct {
+ result Marshaler
+ mu sync.Mutex
+}
+
+func (d *deferred) MarshalGQL(w io.Writer) {
+ d.mu.Lock()
+ d.result.MarshalGQL(w)
+ d.mu.Unlock()
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/error.go b/vendor/github.com/vektah/gqlgen/graphql/error.go
new file mode 100644
index 00000000..15e65fab
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/error.go
@@ -0,0 +1,46 @@
+package graphql
+
+import (
+ "context"
+)
+
+// Error is the standard graphql error type described in https://facebook.github.io/graphql/draft/#sec-Errors
+type Error struct {
+ Message string `json:"message"`
+ Path []interface{} `json:"path,omitempty"`
+ Locations []ErrorLocation `json:"locations,omitempty"`
+ Extensions map[string]interface{} `json:"extensions,omitempty"`
+}
+
+func (e *Error) Error() string {
+ return e.Message
+}
+
+type ErrorLocation struct {
+ Line int `json:"line,omitempty"`
+ Column int `json:"column,omitempty"`
+}
+
+type ErrorPresenterFunc func(context.Context, error) *Error
+
+type ExtendedError interface {
+ Extensions() map[string]interface{}
+}
+
+func DefaultErrorPresenter(ctx context.Context, err error) *Error {
+ if gqlerr, ok := err.(*Error); ok {
+ gqlerr.Path = GetResolverContext(ctx).Path
+ return gqlerr
+ }
+
+ var extensions map[string]interface{}
+ if ee, ok := err.(ExtendedError); ok {
+ extensions = ee.Extensions()
+ }
+
+ return &Error{
+ Message: err.Error(),
+ Path: GetResolverContext(ctx).Path,
+ Extensions: extensions,
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/exec.go b/vendor/github.com/vektah/gqlgen/graphql/exec.go
new file mode 100644
index 00000000..2c034888
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/exec.go
@@ -0,0 +1,118 @@
+package graphql
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/vektah/gqlgen/neelance/common"
+ "github.com/vektah/gqlgen/neelance/query"
+ "github.com/vektah/gqlgen/neelance/schema"
+)
+
+type ExecutableSchema interface {
+ Schema() *schema.Schema
+
+ Query(ctx context.Context, op *query.Operation) *Response
+ Mutation(ctx context.Context, op *query.Operation) *Response
+ Subscription(ctx context.Context, op *query.Operation) func() *Response
+}
+
+func CollectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}) []CollectedField {
+ return collectFields(doc, selSet, satisfies, variables, map[string]bool{})
+}
+
+func collectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}, visited map[string]bool) []CollectedField {
+ var groupedFields []CollectedField
+
+ for _, sel := range selSet {
+ switch sel := sel.(type) {
+ case *query.Field:
+ f := getOrCreateField(&groupedFields, sel.Alias.Name, func() CollectedField {
+ f := CollectedField{
+ Alias: sel.Alias.Name,
+ Name: sel.Name.Name,
+ }
+ if len(sel.Arguments) > 0 {
+ f.Args = map[string]interface{}{}
+ for _, arg := range sel.Arguments {
+ if variable, ok := arg.Value.(*common.Variable); ok {
+ if val, ok := variables[variable.Name]; ok {
+ f.Args[arg.Name.Name] = val
+ }
+ } else {
+ f.Args[arg.Name.Name] = arg.Value.Value(variables)
+ }
+ }
+ }
+ return f
+ })
+
+ f.Selections = append(f.Selections, sel.Selections...)
+ case *query.InlineFragment:
+ if !instanceOf(sel.On.Ident.Name, satisfies) {
+ continue
+ }
+
+ for _, childField := range collectFields(doc, sel.Selections, satisfies, variables, visited) {
+ f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
+ f.Selections = append(f.Selections, childField.Selections...)
+ }
+
+ case *query.FragmentSpread:
+ fragmentName := sel.Name.Name
+ if _, seen := visited[fragmentName]; seen {
+ continue
+ }
+ visited[fragmentName] = true
+
+ fragment := doc.Fragments.Get(fragmentName)
+ if fragment == nil {
+ // should never happen, validator has already run
+ panic(fmt.Errorf("missing fragment %s", fragmentName))
+ }
+
+ if !instanceOf(fragment.On.Ident.Name, satisfies) {
+ continue
+ }
+
+ for _, childField := range collectFields(doc, fragment.Selections, satisfies, variables, visited) {
+ f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
+ f.Selections = append(f.Selections, childField.Selections...)
+ }
+
+ default:
+ panic(fmt.Errorf("unsupported %T", sel))
+ }
+ }
+
+ return groupedFields
+}
+
+type CollectedField struct {
+ Alias string
+ Name string
+ Args map[string]interface{}
+ Selections []query.Selection
+}
+
+func instanceOf(val string, satisfies []string) bool {
+ for _, s := range satisfies {
+ if val == s {
+ return true
+ }
+ }
+ return false
+}
+
+func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField {
+ for i, cf := range *c {
+ if cf.Alias == name {
+ return &(*c)[i]
+ }
+ }
+
+ f := creator()
+
+ *c = append(*c, f)
+ return &(*c)[len(*c)-1]
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/float.go b/vendor/github.com/vektah/gqlgen/graphql/float.go
new file mode 100644
index 00000000..c08b490a
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/float.go
@@ -0,0 +1,26 @@
+package graphql
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+)
+
+func MarshalFloat(f float64) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ io.WriteString(w, fmt.Sprintf("%f", f))
+ })
+}
+
+func UnmarshalFloat(v interface{}) (float64, error) {
+ switch v := v.(type) {
+ case string:
+ return strconv.ParseFloat(v, 64)
+ case int:
+ return float64(v), nil
+ case float64:
+ return v, nil
+ default:
+ return 0, fmt.Errorf("%T is not an float", v)
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/id.go b/vendor/github.com/vektah/gqlgen/graphql/id.go
new file mode 100644
index 00000000..7958670c
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/id.go
@@ -0,0 +1,33 @@
+package graphql
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+)
+
+func MarshalID(s string) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ io.WriteString(w, strconv.Quote(s))
+ })
+}
+func UnmarshalID(v interface{}) (string, error) {
+ switch v := v.(type) {
+ case string:
+ return v, nil
+ case int:
+ return strconv.Itoa(v), nil
+ case float64:
+ return fmt.Sprintf("%f", v), nil
+ case bool:
+ if v {
+ return "true", nil
+ } else {
+ return "false", nil
+ }
+ case nil:
+ return "null", nil
+ default:
+ return "", fmt.Errorf("%T is not a string", v)
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/int.go b/vendor/github.com/vektah/gqlgen/graphql/int.go
new file mode 100644
index 00000000..b63b4c2a
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/int.go
@@ -0,0 +1,26 @@
+package graphql
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+)
+
+func MarshalInt(i int) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ io.WriteString(w, strconv.Itoa(i))
+ })
+}
+
+func UnmarshalInt(v interface{}) (int, error) {
+ switch v := v.(type) {
+ case string:
+ return strconv.Atoi(v)
+ case int:
+ return v, nil
+ case float64:
+ return int(v), nil
+ default:
+ return 0, fmt.Errorf("%T is not an int", v)
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/jsonw.go b/vendor/github.com/vektah/gqlgen/graphql/jsonw.go
new file mode 100644
index 00000000..ef9e69c7
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/jsonw.go
@@ -0,0 +1,83 @@
+package graphql
+
+import (
+ "io"
+ "strconv"
+)
+
+var nullLit = []byte(`null`)
+var trueLit = []byte(`true`)
+var falseLit = []byte(`false`)
+var openBrace = []byte(`{`)
+var closeBrace = []byte(`}`)
+var openBracket = []byte(`[`)
+var closeBracket = []byte(`]`)
+var colon = []byte(`:`)
+var comma = []byte(`,`)
+
+var Null = lit(nullLit)
+var True = lit(trueLit)
+var False = lit(falseLit)
+
+type Marshaler interface {
+ MarshalGQL(w io.Writer)
+}
+
+type Unmarshaler interface {
+ UnmarshalGQL(v interface{}) error
+}
+
+type OrderedMap struct {
+ Keys []string
+ Values []Marshaler
+}
+
+type WriterFunc func(writer io.Writer)
+
+func (f WriterFunc) MarshalGQL(w io.Writer) {
+ f(w)
+}
+
+func NewOrderedMap(len int) *OrderedMap {
+ return &OrderedMap{
+ Keys: make([]string, len),
+ Values: make([]Marshaler, len),
+ }
+}
+
+func (m *OrderedMap) Add(key string, value Marshaler) {
+ m.Keys = append(m.Keys, key)
+ m.Values = append(m.Values, value)
+}
+
+func (m *OrderedMap) MarshalGQL(writer io.Writer) {
+ writer.Write(openBrace)
+ for i, key := range m.Keys {
+ if i != 0 {
+ writer.Write(comma)
+ }
+ io.WriteString(writer, strconv.Quote(key))
+ writer.Write(colon)
+ m.Values[i].MarshalGQL(writer)
+ }
+ writer.Write(closeBrace)
+}
+
+type Array []Marshaler
+
+func (a Array) MarshalGQL(writer io.Writer) {
+ writer.Write(openBracket)
+ for i, val := range a {
+ if i != 0 {
+ writer.Write(comma)
+ }
+ val.MarshalGQL(writer)
+ }
+ writer.Write(closeBracket)
+}
+
+func lit(b []byte) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ w.Write(b)
+ })
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/map.go b/vendor/github.com/vektah/gqlgen/graphql/map.go
new file mode 100644
index 00000000..1e91d1d9
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/map.go
@@ -0,0 +1,24 @@
+package graphql
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+)
+
+func MarshalMap(val map[string]interface{}) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ err := json.NewEncoder(w).Encode(val)
+ if err != nil {
+ panic(err)
+ }
+ })
+}
+
+func UnmarshalMap(v interface{}) (map[string]interface{}, error) {
+ if m, ok := v.(map[string]interface{}); ok {
+ return m, nil
+ }
+
+ return nil, fmt.Errorf("%T is not a map", v)
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/oneshot.go b/vendor/github.com/vektah/gqlgen/graphql/oneshot.go
new file mode 100644
index 00000000..dd31f5ba
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/oneshot.go
@@ -0,0 +1,14 @@
+package graphql
+
+func OneShot(resp *Response) func() *Response {
+ var oneshot bool
+
+ return func() *Response {
+ if oneshot {
+ return nil
+ }
+ oneshot = true
+
+ return resp
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/recovery.go b/vendor/github.com/vektah/gqlgen/graphql/recovery.go
new file mode 100644
index 00000000..3aa032dc
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/recovery.go
@@ -0,0 +1,19 @@
+package graphql
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "runtime/debug"
+)
+
+type RecoverFunc func(ctx context.Context, err interface{}) (userMessage error)
+
+func DefaultRecover(ctx context.Context, err interface{}) error {
+ fmt.Fprintln(os.Stderr, err)
+ fmt.Fprintln(os.Stderr)
+ debug.PrintStack()
+
+ return errors.New("internal system error")
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/response.go b/vendor/github.com/vektah/gqlgen/graphql/response.go
new file mode 100644
index 00000000..c0dc1c23
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/response.go
@@ -0,0 +1,18 @@
+package graphql
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+)
+
+type Response struct {
+ Data json.RawMessage `json:"data"`
+ Errors []*Error `json:"errors,omitempty"`
+}
+
+func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response {
+ return &Response{
+ Errors: []*Error{{Message: fmt.Sprintf(messagef, args...)}},
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/string.go b/vendor/github.com/vektah/gqlgen/graphql/string.go
new file mode 100644
index 00000000..d5fb3294
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/string.go
@@ -0,0 +1,63 @@
+package graphql
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+)
+
+const encodeHex = "0123456789ABCDEF"
+
+func MarshalString(s string) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ start := 0
+ io.WriteString(w, `"`)
+
+ for i, c := range s {
+ if c < 0x20 || c == '\\' || c == '"' {
+ io.WriteString(w, s[start:i])
+
+ switch c {
+ case '\t':
+ io.WriteString(w, `\t`)
+ case '\r':
+ io.WriteString(w, `\r`)
+ case '\n':
+ io.WriteString(w, `\n`)
+ case '\\':
+ io.WriteString(w, `\\`)
+ case '"':
+ io.WriteString(w, `\"`)
+ default:
+ io.WriteString(w, `\u00`)
+ w.Write([]byte{encodeHex[c>>4], encodeHex[c&0xf]})
+ }
+
+ start = i + 1
+ }
+ }
+
+ io.WriteString(w, s[start:])
+ io.WriteString(w, `"`)
+ })
+}
+func UnmarshalString(v interface{}) (string, error) {
+ switch v := v.(type) {
+ case string:
+ return v, nil
+ case int:
+ return strconv.Itoa(v), nil
+ case float64:
+ return fmt.Sprintf("%f", v), nil
+ case bool:
+ if v {
+ return "true", nil
+ } else {
+ return "false", nil
+ }
+ case nil:
+ return "null", nil
+ default:
+ return "", fmt.Errorf("%T is not a string", v)
+ }
+}
diff --git a/vendor/github.com/vektah/gqlgen/graphql/time.go b/vendor/github.com/vektah/gqlgen/graphql/time.go
new file mode 100644
index 00000000..4f448560
--- /dev/null
+++ b/vendor/github.com/vektah/gqlgen/graphql/time.go
@@ -0,0 +1,21 @@
+package graphql
+
+import (
+ "errors"
+ "io"
+ "strconv"
+ "time"
+)
+
+func MarshalTime(t time.Time) Marshaler {
+ return WriterFunc(func(w io.Writer) {
+ io.WriteString(w, strconv.Quote(t.Format(time.RFC3339)))
+ })
+}
+
+func UnmarshalTime(v interface{}) (time.Time, error) {
+ if tmpStr, ok := v.(string); ok {
+ return time.Parse(time.RFC3339, tmpStr)
+ }
+ return time.Time{}, errors.New("time should be RFC3339 formatted string")
+}