Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Function - #1
- func getStructInfo(st reflect.Type) (*structInfo, error) {
- structMapMutex.RLock()
- sinfo, found := structMap[st]
- structMapMutex.RUnlock()
- if found {
- return sinfo, nil
- }
- n := st.NumField()
- fieldsMap := make(map[string]fieldInfo)
- fieldsList := make([]fieldInfo, 0, n)
- inlineMap := -1
- for i := 0; i != n; i++ {
- field := st.Field(i)
- if field.PkgPath != "" && !field.Anonymous {
- continue // Private field
- }
- info := fieldInfo{Num: i}
- tag := field.Tag.Get("bson")
- if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
- tag = string(field.Tag)
- }
- if tag == "-" {
- continue
- }
- inline := false
- fields := strings.Split(tag, ",")
- if len(fields) > 1 {
- for _, flag := range fields[1:] {
- switch flag {
- case "omitempty":
- info.OmitEmpty = true
- case "minsize":
- info.MinSize = true
- case "inline":
- inline = true
- default:
- msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
- panic(externalPanic(msg))
- }
- }
- tag = fields[0]
- }
- if inline {
- switch field.Type.Kind() {
- case reflect.Map:
- if inlineMap >= 0 {
- return nil, errors.New("Multiple ,inline maps in struct " + st.String())
- }
- if field.Type.Key() != reflect.TypeOf("") {
- return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
- }
- inlineMap = info.Num
- case reflect.Struct:
- sinfo, err := getStructInfo(field.Type)
- if err != nil {
- return nil, err
- }
- for _, finfo := range sinfo.FieldsList {
- if _, found := fieldsMap[finfo.Key]; found {
- msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- if finfo.Inline == nil {
- finfo.Inline = []int{i, finfo.Num}
- } else {
- finfo.Inline = append([]int{i}, finfo.Inline...)
- }
- fieldsMap[finfo.Key] = finfo
- fieldsList = append(fieldsList, finfo)
- }
- default:
- panic("Option ,inline needs a struct value or map field")
- }
- continue
- }
- if tag != "" {
- info.Key = tag
- } else {
- info.Key = strings.ToLower(field.Name)
- }
- if _, found = fieldsMap[info.Key]; found {
- msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- fieldsList = append(fieldsList, info)
- fieldsMap[info.Key] = info
- }
- sinfo = &structInfo{
- fieldsMap,
- fieldsList,
- inlineMap,
- reflect.New(st).Elem(),
- }
- structMapMutex.Lock()
- structMap[st] = sinfo
- structMapMutex.Unlock()
- return sinfo, nil
- }
- // Function - #2
- func (e *encoder) addStruct(v reflect.Value) {
- sinfo, err := getStructInfo(v.Type())
- if err != nil {
- panic(err)
- }
- var value reflect.Value
- if sinfo.InlineMap >= 0 {
- m := v.Field(sinfo.InlineMap)
- if m.Len() > 0 {
- for _, k := range m.MapKeys() {
- ks := k.String()
- if _, found := sinfo.FieldsMap[ks]; found {
- panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks))
- }
- e.addElem(ks, m.MapIndex(k), false)
- }
- }
- }
- for _, info := range sinfo.FieldsList {
- if info.Inline == nil {
- value = v.Field(info.Num)
- } else {
- // as pointers to struct are allowed here,
- // there is no guarantee that pointer won't be nil.
- //
- // It is expected allowed behaviour
- // so info.Inline MAY consist index to a nil pointer
- // and that is why we safely call v.FieldByIndex and just continue on panic
- field, errField := safeFieldByIndex(v, info.Inline)
- if errField != nil {
- continue
- }
- value = field
- }
- if info.OmitEmpty && isZero(value) {
- continue
- }
- if useRespectNilValues &&
- (value.Kind() == reflect.Slice || value.Kind() == reflect.Map) &&
- value.IsNil() {
- e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize)
- continue
- }
- e.addElem(info.Key, value, info.MinSize)
- }
- }
Advertisement