github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/encoding/graphql/decode.go (about)

     1  // Copyright ©2017 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package graphql
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  
    13  	"github.com/jingcheng-WU/gonum/graph"
    14  	"github.com/jingcheng-WU/gonum/graph/encoding"
    15  )
    16  
    17  // Unmarshal parses the JSON-encoded data and stores the result in dst.
    18  // Node IDs are obtained from the JSON fields identified by the uid parameter.
    19  // UIDs obtained from the JSON encoding must map to unique node ID values
    20  // consistently across the JSON-encoded spanning tree.
    21  func Unmarshal(data []byte, uid string, dst encoding.Builder) error {
    22  	if uid == "" {
    23  		return errors.New("graphql: invalid UID field name")
    24  	}
    25  	var src json.RawMessage
    26  	err := json.Unmarshal(data, &src)
    27  	if err != nil {
    28  		return err
    29  	}
    30  	gen := generator{dst: dst, uidName: uid, nodes: make(map[string]graph.Node)}
    31  	return gen.walk(src, nil, "")
    32  }
    33  
    34  // StringIDSetter is a graph node that can set its ID based on the given uid string.
    35  type StringIDSetter interface {
    36  	SetIDFromString(uid string) error
    37  }
    38  
    39  // LabelSetter is a graph edge that can set its label.
    40  type LabelSetter interface {
    41  	SetLabel(string)
    42  }
    43  
    44  type generator struct {
    45  	dst encoding.Builder
    46  
    47  	// uidName is the name of the UID field in the source JSON.
    48  	uidName string
    49  	// nodes maps from GraphQL UID string to graph.Node.
    50  	nodes map[string]graph.Node
    51  }
    52  
    53  func (g *generator) walk(src json.RawMessage, node graph.Node, attr string) error {
    54  	switch src[0] {
    55  	case '{':
    56  		var val map[string]json.RawMessage
    57  		err := json.Unmarshal(src, &val)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		if next, ok := val[g.uidName]; !ok {
    62  			if node != nil {
    63  				var buf bytes.Buffer
    64  				err := json.Compact(&buf, src)
    65  				if err != nil {
    66  					panic(err)
    67  				}
    68  				return fmt.Errorf("graphql: no UID for node: `%s`", &buf)
    69  			}
    70  		} else {
    71  			var v interface{}
    72  			err = json.Unmarshal(next, &v)
    73  			if err != nil {
    74  				return err
    75  			}
    76  			value := fmt.Sprint(v)
    77  			child, ok := g.nodes[value]
    78  			if !ok {
    79  				child = g.dst.NewNode()
    80  				s, ok := child.(StringIDSetter)
    81  				if !ok {
    82  					return errors.New("graphql: cannot set UID")
    83  				}
    84  				err = s.SetIDFromString(value)
    85  				if err != nil {
    86  					return err
    87  				}
    88  				g.nodes[value] = child
    89  				g.dst.AddNode(child)
    90  			}
    91  			if node != nil {
    92  				e := g.dst.NewEdge(node, child)
    93  				if s, ok := e.(LabelSetter); ok {
    94  					s.SetLabel(attr)
    95  				}
    96  				g.dst.SetEdge(e)
    97  			}
    98  			node = child
    99  		}
   100  		for attr, src := range val {
   101  			if attr == g.uidName {
   102  				continue
   103  			}
   104  			err = g.walk(src, node, attr)
   105  			if err != nil {
   106  				return err
   107  			}
   108  		}
   109  
   110  	case '[':
   111  		var val []json.RawMessage
   112  		err := json.Unmarshal(src, &val)
   113  		if err != nil {
   114  			return err
   115  		}
   116  		for _, src := range val {
   117  			err = g.walk(src, node, attr)
   118  			if err != nil {
   119  				return err
   120  			}
   121  		}
   122  
   123  	default:
   124  		var v interface{}
   125  		err := json.Unmarshal(src, &v)
   126  		if err != nil {
   127  			return err
   128  		}
   129  		if attr == g.uidName {
   130  			value := fmt.Sprint(v)
   131  			if s, ok := node.(StringIDSetter); ok {
   132  				if _, ok := g.nodes[value]; !ok {
   133  					err = s.SetIDFromString(value)
   134  					if err != nil {
   135  						return err
   136  					}
   137  					g.nodes[value] = node
   138  				}
   139  			} else {
   140  				return errors.New("graphql: cannot set ID")
   141  			}
   142  		} else if s, ok := node.(encoding.AttributeSetter); ok {
   143  			var value string
   144  			if _, ok := v.(float64); ok {
   145  				value = string(src)
   146  			} else {
   147  				value = fmt.Sprint(v)
   148  			}
   149  			err = s.SetAttribute(encoding.Attribute{Key: attr, Value: value})
   150  			if err != nil {
   151  				return err
   152  			}
   153  		}
   154  	}
   155  
   156  	return nil
   157  }