git.gammaspectra.live/P2Pool/go-json@v0.99.0/internal/encoder/query.go (about)

     1  package encoder
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  var (
    10  	Marshal   func(interface{}) ([]byte, error)
    11  	Unmarshal func([]byte, interface{}) error
    12  )
    13  
    14  type FieldQuery struct {
    15  	Name   string
    16  	Fields []*FieldQuery
    17  	hash   string
    18  }
    19  
    20  func (q *FieldQuery) Hash() string {
    21  	if q.hash != "" {
    22  		return q.hash
    23  	}
    24  	b, _ := Marshal(q)
    25  	q.hash = string(b)
    26  	return q.hash
    27  }
    28  
    29  func (q *FieldQuery) MarshalJSON() ([]byte, error) {
    30  	if q.Name != "" {
    31  		if len(q.Fields) > 0 {
    32  			return Marshal(map[string][]*FieldQuery{q.Name: q.Fields})
    33  		}
    34  		return Marshal(q.Name)
    35  	}
    36  	return Marshal(q.Fields)
    37  }
    38  
    39  func (q *FieldQuery) QueryString() (FieldQueryString, error) {
    40  	b, err := Marshal(q)
    41  	if err != nil {
    42  		return "", err
    43  	}
    44  	return FieldQueryString(b), nil
    45  }
    46  
    47  type FieldQueryString string
    48  
    49  func (s FieldQueryString) Build() (*FieldQuery, error) {
    50  	var query interface{}
    51  	if err := Unmarshal([]byte(s), &query); err != nil {
    52  		return nil, err
    53  	}
    54  	return s.build(reflect.ValueOf(query))
    55  }
    56  
    57  func (s FieldQueryString) build(v reflect.Value) (*FieldQuery, error) {
    58  	switch v.Type().Kind() {
    59  	case reflect.String:
    60  		return s.buildString(v)
    61  	case reflect.Map:
    62  		return s.buildMap(v)
    63  	case reflect.Slice:
    64  		return s.buildSlice(v)
    65  	case reflect.Interface:
    66  		return s.build(reflect.ValueOf(v.Interface()))
    67  	}
    68  	return nil, fmt.Errorf("failed to build field query")
    69  }
    70  
    71  func (s FieldQueryString) buildString(v reflect.Value) (*FieldQuery, error) {
    72  	b := []byte(v.String())
    73  	switch b[0] {
    74  	case '[', '{':
    75  		var query interface{}
    76  		if err := Unmarshal(b, &query); err != nil {
    77  			return nil, err
    78  		}
    79  		if str, ok := query.(string); ok {
    80  			return &FieldQuery{Name: str}, nil
    81  		}
    82  		return s.build(reflect.ValueOf(query))
    83  	}
    84  	return &FieldQuery{Name: string(b)}, nil
    85  }
    86  
    87  func (s FieldQueryString) buildSlice(v reflect.Value) (*FieldQuery, error) {
    88  	fields := make([]*FieldQuery, 0, v.Len())
    89  	for i := 0; i < v.Len(); i++ {
    90  		def, err := s.build(v.Index(i))
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		fields = append(fields, def)
    95  	}
    96  	return &FieldQuery{Fields: fields}, nil
    97  }
    98  
    99  func (s FieldQueryString) buildMap(v reflect.Value) (*FieldQuery, error) {
   100  	keys := v.MapKeys()
   101  	if len(keys) != 1 {
   102  		return nil, fmt.Errorf("failed to build field query object")
   103  	}
   104  	key := keys[0]
   105  	if key.Type().Kind() != reflect.String {
   106  		return nil, fmt.Errorf("failed to build field query. invalid object key type")
   107  	}
   108  	name := key.String()
   109  	def, err := s.build(v.MapIndex(key))
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return &FieldQuery{
   114  		Name:   name,
   115  		Fields: def.Fields,
   116  	}, nil
   117  }
   118  
   119  type queryKey struct{}
   120  
   121  func FieldQueryFromContext(ctx context.Context) *FieldQuery {
   122  	query := ctx.Value(queryKey{})
   123  	if query == nil {
   124  		return nil
   125  	}
   126  	q, ok := query.(*FieldQuery)
   127  	if !ok {
   128  		return nil
   129  	}
   130  	return q
   131  }
   132  
   133  func SetFieldQueryToContext(ctx context.Context, query *FieldQuery) context.Context {
   134  	return context.WithValue(ctx, queryKey{}, query)
   135  }