| package encoder |
| |
| import ( |
| "context" |
| "fmt" |
| "reflect" |
| ) |
| |
| var ( |
| Marshal func(interface{}) ([]byte, error) |
| Unmarshal func([]byte, interface{}) error |
| ) |
| |
| type FieldQuery struct { |
| Name string |
| Fields []*FieldQuery |
| hash string |
| } |
| |
| func (q *FieldQuery) Hash() string { |
| if q.hash != "" { |
| return q.hash |
| } |
| b, _ := Marshal(q) |
| q.hash = string(b) |
| return q.hash |
| } |
| |
| func (q *FieldQuery) MarshalJSON() ([]byte, error) { |
| if q.Name != "" { |
| if len(q.Fields) > 0 { |
| return Marshal(map[string][]*FieldQuery{q.Name: q.Fields}) |
| } |
| return Marshal(q.Name) |
| } |
| return Marshal(q.Fields) |
| } |
| |
| func (q *FieldQuery) QueryString() (FieldQueryString, error) { |
| b, err := Marshal(q) |
| if err != nil { |
| return "", err |
| } |
| return FieldQueryString(b), nil |
| } |
| |
| type FieldQueryString string |
| |
| func (s FieldQueryString) Build() (*FieldQuery, error) { |
| var query interface{} |
| if err := Unmarshal([]byte(s), &query); err != nil { |
| return nil, err |
| } |
| return s.build(reflect.ValueOf(query)) |
| } |
| |
| func (s FieldQueryString) build(v reflect.Value) (*FieldQuery, error) { |
| switch v.Type().Kind() { |
| case reflect.String: |
| return s.buildString(v) |
| case reflect.Map: |
| return s.buildMap(v) |
| case reflect.Slice: |
| return s.buildSlice(v) |
| case reflect.Interface: |
| return s.build(reflect.ValueOf(v.Interface())) |
| } |
| return nil, fmt.Errorf("failed to build field query") |
| } |
| |
| func (s FieldQueryString) buildString(v reflect.Value) (*FieldQuery, error) { |
| b := []byte(v.String()) |
| switch b[0] { |
| case '[', '{': |
| var query interface{} |
| if err := Unmarshal(b, &query); err != nil { |
| return nil, err |
| } |
| if str, ok := query.(string); ok { |
| return &FieldQuery{Name: str}, nil |
| } |
| return s.build(reflect.ValueOf(query)) |
| } |
| return &FieldQuery{Name: string(b)}, nil |
| } |
| |
| func (s FieldQueryString) buildSlice(v reflect.Value) (*FieldQuery, error) { |
| fields := make([]*FieldQuery, 0, v.Len()) |
| for i := 0; i < v.Len(); i++ { |
| def, err := s.build(v.Index(i)) |
| if err != nil { |
| return nil, err |
| } |
| fields = append(fields, def) |
| } |
| return &FieldQuery{Fields: fields}, nil |
| } |
| |
| func (s FieldQueryString) buildMap(v reflect.Value) (*FieldQuery, error) { |
| keys := v.MapKeys() |
| if len(keys) != 1 { |
| return nil, fmt.Errorf("failed to build field query object") |
| } |
| key := keys[0] |
| if key.Type().Kind() != reflect.String { |
| return nil, fmt.Errorf("failed to build field query. invalid object key type") |
| } |
| name := key.String() |
| def, err := s.build(v.MapIndex(key)) |
| if err != nil { |
| return nil, err |
| } |
| return &FieldQuery{ |
| Name: name, |
| Fields: def.Fields, |
| }, nil |
| } |
| |
| type queryKey struct{} |
| |
| func FieldQueryFromContext(ctx context.Context) *FieldQuery { |
| query := ctx.Value(queryKey{}) |
| if query == nil { |
| return nil |
| } |
| q, ok := query.(*FieldQuery) |
| if !ok { |
| return nil |
| } |
| return q |
| } |
| |
| func SetFieldQueryToContext(ctx context.Context, query *FieldQuery) context.Context { |
| return context.WithValue(ctx, queryKey{}, query) |
| } |