diff options
author | Michael Muré <batolettre@gmail.com> | 2020-02-05 22:03:19 +0100 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2020-02-05 22:33:03 +0100 |
commit | 1d4bb7ceb0cef79d68df0bacc913b01e40e6ddd6 (patch) | |
tree | e088b0fa43058afde1db71541d8fcb4b94905d6e /vendor/github.com/shurcooL/graphql/internal | |
parent | f093be96e98284580d61664adecd0a2ff8b354e4 (diff) | |
download | git-bug-1d4bb7ceb0cef79d68df0bacc913b01e40e6ddd6.tar.gz |
migrate to go modules
Diffstat (limited to 'vendor/github.com/shurcooL/graphql/internal')
-rw-r--r-- | vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go | 308 |
1 files changed, 0 insertions, 308 deletions
diff --git a/vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go b/vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go deleted file mode 100644 index 9e75791d..00000000 --- a/vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go +++ /dev/null @@ -1,308 +0,0 @@ -// Package jsonutil provides a function for decoding JSON -// into a GraphQL query data structure. -package jsonutil - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "reflect" - "strings" -) - -// UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores -// the result in the GraphQL query data structure pointed to by v. -// -// The implementation is created on top of the JSON tokenizer available -// in "encoding/json".Decoder. -func UnmarshalGraphQL(data []byte, v interface{}) error { - dec := json.NewDecoder(bytes.NewReader(data)) - dec.UseNumber() - err := (&decoder{tokenizer: dec}).Decode(v) - if err != nil { - return err - } - tok, err := dec.Token() - switch err { - case io.EOF: - // Expect to get io.EOF. There shouldn't be any more - // tokens left after we've decoded v successfully. - return nil - case nil: - return fmt.Errorf("invalid token '%v' after top-level value", tok) - default: - return err - } -} - -// decoder is a JSON decoder that performs custom unmarshaling behavior -// for GraphQL query data structures. It's implemented on top of a JSON tokenizer. -type decoder struct { - tokenizer interface { - Token() (json.Token, error) - } - - // Stack of what part of input JSON we're in the middle of - objects, arrays. - parseState []json.Delim - - // Stacks of values where to unmarshal. - // The top of each stack is the reflect.Value where to unmarshal next JSON value. - // - // The reason there's more than one stack is because we might be unmarshaling - // a single JSON value into multiple GraphQL fragments or embedded structs, so - // we keep track of them all. - vs [][]reflect.Value -} - -// Decode decodes a single JSON value from d.tokenizer into v. -func (d *decoder) Decode(v interface{}) error { - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr { - return fmt.Errorf("cannot decode into non-pointer %T", v) - } - d.vs = [][]reflect.Value{{rv.Elem()}} - return d.decode() -} - -// decode decodes a single JSON value from d.tokenizer into d.vs. -func (d *decoder) decode() error { - // The loop invariant is that the top of each d.vs stack - // is where we try to unmarshal the next JSON value we see. - for len(d.vs) > 0 { - tok, err := d.tokenizer.Token() - if err == io.EOF { - return errors.New("unexpected end of JSON input") - } else if err != nil { - return err - } - - switch { - - // Are we inside an object and seeing next key (rather than end of object)? - case d.state() == '{' && tok != json.Delim('}'): - key, ok := tok.(string) - if !ok { - return errors.New("unexpected non-key in JSON input") - } - someFieldExist := false - for i := range d.vs { - v := d.vs[i][len(d.vs[i])-1] - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - var f reflect.Value - if v.Kind() == reflect.Struct { - f = fieldByGraphQLName(v, key) - if f.IsValid() { - someFieldExist = true - } - } - d.vs[i] = append(d.vs[i], f) - } - if !someFieldExist { - return fmt.Errorf("struct field for %s doesn't exist in any of %v places to unmarshal", key, len(d.vs)) - } - - // We've just consumed the current token, which was the key. - // Read the next token, which should be the value, and let the rest of code process it. - tok, err = d.tokenizer.Token() - if err == io.EOF { - return errors.New("unexpected end of JSON input") - } else if err != nil { - return err - } - - // Are we inside an array and seeing next value (rather than end of array)? - case d.state() == '[' && tok != json.Delim(']'): - someSliceExist := false - for i := range d.vs { - v := d.vs[i][len(d.vs[i])-1] - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - var f reflect.Value - if v.Kind() == reflect.Slice { - v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) // v = append(v, T). - f = v.Index(v.Len() - 1) - someSliceExist = true - } - d.vs[i] = append(d.vs[i], f) - } - if !someSliceExist { - return fmt.Errorf("slice doesn't exist in any of %v places to unmarshal", len(d.vs)) - } - } - - switch tok := tok.(type) { - case string, json.Number, bool, nil: - // Value. - - for i := range d.vs { - v := d.vs[i][len(d.vs[i])-1] - if !v.IsValid() { - continue - } - err := unmarshalValue(tok, v) - if err != nil { - return err - } - } - d.popAllVs() - - case json.Delim: - switch tok { - case '{': - // Start of object. - - d.pushState(tok) - - frontier := make([]reflect.Value, len(d.vs)) // Places to look for GraphQL fragments/embedded structs. - for i := range d.vs { - v := d.vs[i][len(d.vs[i])-1] - frontier[i] = v - // TODO: Do this recursively or not? Add a test case if needed. - if v.Kind() == reflect.Ptr && v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) // v = new(T). - } - } - // Find GraphQL fragments/embedded structs recursively, adding to frontier - // as new ones are discovered and exploring them further. - for len(frontier) > 0 { - v := frontier[0] - frontier = frontier[1:] - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - if v.Kind() != reflect.Struct { - continue - } - for i := 0; i < v.NumField(); i++ { - if isGraphQLFragment(v.Type().Field(i)) || v.Type().Field(i).Anonymous { - // Add GraphQL fragment or embedded struct. - d.vs = append(d.vs, []reflect.Value{v.Field(i)}) - frontier = append(frontier, v.Field(i)) - } - } - } - case '[': - // Start of array. - - d.pushState(tok) - - for i := range d.vs { - v := d.vs[i][len(d.vs[i])-1] - // TODO: Confirm this is needed, write a test case. - //if v.Kind() == reflect.Ptr && v.IsNil() { - // v.Set(reflect.New(v.Type().Elem())) // v = new(T). - //} - - // Reset slice to empty (in case it had non-zero initial value). - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - if v.Kind() != reflect.Slice { - continue - } - v.Set(reflect.MakeSlice(v.Type(), 0, 0)) // v = make(T, 0, 0). - } - case '}', ']': - // End of object or array. - d.popAllVs() - d.popState() - default: - return errors.New("unexpected delimiter in JSON input") - } - default: - return errors.New("unexpected token in JSON input") - } - } - return nil -} - -// pushState pushes a new parse state s onto the stack. -func (d *decoder) pushState(s json.Delim) { - d.parseState = append(d.parseState, s) -} - -// popState pops a parse state (already obtained) off the stack. -// The stack must be non-empty. -func (d *decoder) popState() { - d.parseState = d.parseState[:len(d.parseState)-1] -} - -// state reports the parse state on top of stack, or 0 if empty. -func (d *decoder) state() json.Delim { - if len(d.parseState) == 0 { - return 0 - } - return d.parseState[len(d.parseState)-1] -} - -// popAllVs pops from all d.vs stacks, keeping only non-empty ones. -func (d *decoder) popAllVs() { - var nonEmpty [][]reflect.Value - for i := range d.vs { - d.vs[i] = d.vs[i][:len(d.vs[i])-1] - if len(d.vs[i]) > 0 { - nonEmpty = append(nonEmpty, d.vs[i]) - } - } - d.vs = nonEmpty -} - -// fieldByGraphQLName returns a struct field of struct v that matches GraphQL name, -// or invalid reflect.Value if none found. -func fieldByGraphQLName(v reflect.Value, name string) reflect.Value { - for i := 0; i < v.NumField(); i++ { - if hasGraphQLName(v.Type().Field(i), name) { - return v.Field(i) - } - } - return reflect.Value{} -} - -// hasGraphQLName reports whether struct field f has GraphQL name. -func hasGraphQLName(f reflect.StructField, name string) bool { - value, ok := f.Tag.Lookup("graphql") - if !ok { - // TODO: caseconv package is relatively slow. Optimize it, then consider using it here. - //return caseconv.MixedCapsToLowerCamelCase(f.Name) == name - return strings.EqualFold(f.Name, name) - } - value = strings.TrimSpace(value) // TODO: Parse better. - if strings.HasPrefix(value, "...") { - // GraphQL fragment. It doesn't have a name. - return false - } - if i := strings.Index(value, "("); i != -1 { - value = value[:i] - } - if i := strings.Index(value, ":"); i != -1 { - value = value[:i] - } - return strings.TrimSpace(value) == name -} - -// isGraphQLFragment reports whether struct field f is a GraphQL fragment. -func isGraphQLFragment(f reflect.StructField) bool { - value, ok := f.Tag.Lookup("graphql") - if !ok { - return false - } - value = strings.TrimSpace(value) // TODO: Parse better. - return strings.HasPrefix(value, "...") -} - -// unmarshalValue unmarshals JSON value into v. -func unmarshalValue(value json.Token, v reflect.Value) error { - b, err := json.Marshal(value) // TODO: Short-circuit (if profiling says it's worth it). - if err != nil { - return err - } - if !v.CanAddr() { - return fmt.Errorf("value %v is not addressable", v) - } - return json.Unmarshal(b, v.Addr().Interface()) -} |