github.com/cornelk/go-cloud@v0.17.1/docstore/driver/document.go (about)

     1  // Copyright 2019 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package driver
    16  
    17  import (
    18  	"reflect"
    19  
    20  	"github.com/cornelk/go-cloud/docstore/internal/fields"
    21  	"github.com/cornelk/go-cloud/gcerrors"
    22  	"github.com/cornelk/go-cloud/internal/gcerr"
    23  )
    24  
    25  // A Document is a lightweight wrapper around either a map[string]interface{} or a
    26  // struct pointer. It provides operations to get and set fields and field paths.
    27  type Document struct {
    28  	Origin interface{}            // the argument to NewDocument
    29  	m      map[string]interface{} // nil if it's a *struct
    30  	s      reflect.Value          // the struct reflected
    31  	fields fields.List            // for structs
    32  }
    33  
    34  // NewDocument creates a new document from doc, which must be a non-nil
    35  // map[string]interface{} or struct pointer.
    36  func NewDocument(doc interface{}) (Document, error) {
    37  	if doc == nil {
    38  		return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document cannot be nil")
    39  	}
    40  	if m, ok := doc.(map[string]interface{}); ok {
    41  		if m == nil {
    42  			return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document map cannot be nil")
    43  		}
    44  		return Document{Origin: doc, m: m}, nil
    45  	}
    46  	v := reflect.ValueOf(doc)
    47  	t := v.Type()
    48  	if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
    49  		return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "expecting *struct or map[string]interface{}, got %s", t)
    50  	}
    51  	t = t.Elem()
    52  	if v.IsNil() {
    53  		return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document struct pointer cannot be nil")
    54  	}
    55  	fields, err := fieldCache.Fields(t)
    56  	if err != nil {
    57  		return Document{}, err
    58  	}
    59  	return Document{Origin: doc, s: v.Elem(), fields: fields}, nil
    60  }
    61  
    62  // GetField returns the value of the named document field.
    63  func (d Document) GetField(field string) (interface{}, error) {
    64  	if d.m != nil {
    65  		x, ok := d.m[field]
    66  		if !ok {
    67  			return nil, gcerr.Newf(gcerr.NotFound, nil, "field %q not found in map", field)
    68  		}
    69  		return x, nil
    70  	} else {
    71  		v, err := d.structField(field)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		return v.Interface(), nil
    76  	}
    77  }
    78  
    79  // getDocument gets the value of the given field path, which must be a document.
    80  // If create is true, it creates intermediate documents as needed.
    81  func (d Document) getDocument(fp []string, create bool) (Document, error) {
    82  	if len(fp) == 0 {
    83  		return d, nil
    84  	}
    85  	x, err := d.GetField(fp[0])
    86  	if err != nil {
    87  		if create && gcerrors.Code(err) == gcerrors.NotFound {
    88  			// TODO(jba): create the right type for the struct field.
    89  			x = map[string]interface{}{}
    90  			if err := d.SetField(fp[0], x); err != nil {
    91  				return Document{}, err
    92  			}
    93  		} else {
    94  			return Document{}, err
    95  		}
    96  	}
    97  	d2, err := NewDocument(x)
    98  	if err != nil {
    99  		return Document{}, err
   100  	}
   101  	return d2.getDocument(fp[1:], create)
   102  }
   103  
   104  // Get returns the value of the given field path in the document.
   105  func (d Document) Get(fp []string) (interface{}, error) {
   106  	d2, err := d.getDocument(fp[:len(fp)-1], false)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	return d2.GetField(fp[len(fp)-1])
   111  }
   112  
   113  func (d Document) structField(name string) (reflect.Value, error) {
   114  	// We do case-insensitive match here to cover the MongoDB's lowercaseFields
   115  	// option.
   116  	f := d.fields.MatchFold(name)
   117  	if f == nil {
   118  		return reflect.Value{}, gcerr.Newf(gcerr.NotFound, nil, "field %q not found in struct type %s", name, d.s.Type())
   119  	}
   120  	fv, ok := fieldByIndex(d.s, f.Index)
   121  	if !ok {
   122  		return reflect.Value{}, gcerr.Newf(gcerr.InvalidArgument, nil, "nil embedded pointer; cannot get field %q from %s",
   123  			name, d.s.Type())
   124  	}
   125  	return fv, nil
   126  }
   127  
   128  // Set sets the value of the field path in the document.
   129  // This creates sub-maps as necessary, if possible.
   130  func (d Document) Set(fp []string, val interface{}) error {
   131  	d2, err := d.getDocument(fp[:len(fp)-1], true)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return d2.SetField(fp[len(fp)-1], val)
   136  }
   137  
   138  // SetField sets the field to value in the document.
   139  func (d Document) SetField(field string, value interface{}) error {
   140  	if d.m != nil {
   141  		d.m[field] = value
   142  		return nil
   143  	}
   144  	v, err := d.structField(field)
   145  	if err != nil {
   146  		return err
   147  	}
   148  	if !v.CanSet() {
   149  		return gcerr.Newf(gcerr.InvalidArgument, nil, "cannot set field %s in struct of type %s: not addressable",
   150  			field, d.s.Type())
   151  	}
   152  	v.Set(reflect.ValueOf(value))
   153  	return nil
   154  }
   155  
   156  // FieldNames returns names of the top-level fields of d.
   157  func (d Document) FieldNames() []string {
   158  	var names []string
   159  	if d.m != nil {
   160  		for k := range d.m {
   161  			names = append(names, k)
   162  		}
   163  	} else {
   164  		for _, f := range d.fields {
   165  			names = append(names, f.Name)
   166  		}
   167  	}
   168  	return names
   169  }
   170  
   171  // Encode encodes the document using the given Encoder.
   172  func (d Document) Encode(e Encoder) error {
   173  	if d.m != nil {
   174  		return encodeMap(reflect.ValueOf(d.m), e)
   175  	}
   176  	return encodeStructWithFields(d.s, d.fields, e)
   177  }
   178  
   179  // Decode decodes the document using the given Decoder.
   180  func (d Document) Decode(dec Decoder) error {
   181  	if d.m != nil {
   182  		return decodeMap(reflect.ValueOf(d.m), dec)
   183  	}
   184  	return decodeStruct(d.s, dec)
   185  }
   186  
   187  // HasField returns whether or not d has a certain field.
   188  func (d Document) HasField(field string) bool {
   189  	return d.hasField(field, true)
   190  }
   191  
   192  // HasFieldFold is like HasField but matches case-insensitively for struct
   193  // field.
   194  func (d Document) HasFieldFold(field string) bool {
   195  	return d.hasField(field, false)
   196  }
   197  
   198  func (d Document) hasField(field string, exactMatch bool) bool {
   199  	if d.m != nil {
   200  		_, ok := d.m[field]
   201  		return ok
   202  	}
   203  	if exactMatch {
   204  		return d.fields.MatchExact(field) != nil
   205  	}
   206  	return d.fields.MatchFold(field) != nil
   207  }