Преглед изворни кода

Add support for ,inline flag with struct values.

This also adds the code to do inlining of maps, as supported by bson
(the code was copied from mgo/bson), but it's disabled for the moment
as I'll need more time to implement it.
Gustavo Niemeyer пре 13 година
родитељ
комит
8691640bc7
5 измењених фајлова са 82 додато и 26 уклоњено
  1. 7 1
      decode.go
  2. 9 0
      decode_test.go
  3. 6 1
      encode.go
  4. 9 0
      encode_test.go
  5. 51 24
      goyaml.go

+ 7 - 1
decode.go

@@ -445,7 +445,13 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
 			continue
 		}
 		if info, ok := fieldsMap[name.String()]; ok {
-			d.unmarshal(n.children[i+1], out.Field(info.Num))
+			var field reflect.Value
+			if info.Inline == nil {
+				field = out.Field(info.Num)
+			} else {
+				field = out.FieldByIndex(info.Inline)
+			}
+			d.unmarshal(n.children[i+1], field)
 		}
 	}
 	return true

+ 9 - 0
decode_test.go

@@ -317,6 +317,15 @@ var unmarshalTests = []struct {
 			B int "-"
 		}{1, 0},
 	},
+
+	// Struct inlining
+	{
+		"a: 1\nb: 2\n",
+		&struct {
+			A int
+			C struct{ B int } `yaml:",inline"`
+		}{1, struct{ B int }{2}},
+	},
 }
 
 func (s *S) TestUnmarshal(c *C) {

+ 6 - 1
encode.go

@@ -115,7 +115,12 @@ func (e *encoder) structv(tag string, in reflect.Value) {
 	}
 	e.mappingv(tag, func() {
 		for _, info := range fields.List {
-			value := in.Field(info.Num)
+			var value reflect.Value
+			if info.Inline == nil {
+				value = in.Field(info.Num)
+			} else {
+				value = in.FieldByIndex(info.Inline)
+			}
 			if info.OmitEmpty && isZero(value) {
 				continue
 			}

+ 9 - 0
encode_test.go

@@ -200,6 +200,15 @@ var marshalTests = []struct {
 		}{1, 2},
 		"a: 1\n",
 	},
+
+	// Struct inlining
+	{
+		&struct {
+			A int
+			C struct{ B int } `yaml:",inline"`
+		}{1, struct{ B int }{2}},
+		"a: 1\nb: 2\n",
+	},
 }
 
 func (s *S) TestMarshal(c *C) {

+ 51 - 24
goyaml.go

@@ -119,6 +119,10 @@ func Unmarshal(in []byte, out interface{}) (err error) {
 //     flow         Marshal using a flow style (useful for structs,
 //                  sequences and maps.
 //
+//     inline       Inline the struct it's applied to, so its fields
+//                  are processed as if they were part of the outer
+//                  struct.
+//
 // In addition, if the key is "-", the field is ignored.
 //
 // For example:
@@ -131,7 +135,7 @@ func Unmarshal(in []byte, out interface{}) (err error) {
 //     goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
 //
 func Marshal(in interface{}) (out []byte, err error) {
-	//defer handleErr(&err)
+	defer handleErr(&err)
 	e := newEncoder()
 	defer e.destroy()
 	e.marshal("", reflect.ValueOf(in))
@@ -146,8 +150,9 @@ func Marshal(in interface{}) (out []byte, err error) {
 // The code in this section was copied from gobson.
 
 type structFields struct {
-	Map  map[string]fieldInfo
-	List []fieldInfo
+	Map       map[string]fieldInfo
+	List      []fieldInfo
+	InlineMap int
 }
 
 type fieldInfo struct {
@@ -155,6 +160,7 @@ type fieldInfo struct {
 	Num       int
 	OmitEmpty bool
 	Flow      bool
+	Inline    []int
 }
 
 var fieldMap = make(map[reflect.Type]*structFields)
@@ -176,7 +182,8 @@ func getStructFields(st reflect.Type) (*structFields, error) {
 
 	n := st.NumField()
 	fieldsMap := make(map[string]fieldInfo)
-	fieldsList := make([]fieldInfo, n)
+	fieldsList := make([]fieldInfo, 0, n)
+	inlineMap := -1
 	for i := 0; i != n; i++ {
 		field := st.Field(i)
 		if field.PkgPath != "" {
@@ -193,24 +200,7 @@ func getStructFields(st reflect.Type) (*structFields, error) {
 			continue
 		}
 
-		// XXX Drop this after a few releases.
-		if s := strings.Index(tag, "/"); s >= 0 {
-			recommend := tag[:s]
-			for _, c := range tag[s+1:] {
-				switch c {
-				case 'c':
-					recommend += ",omitempty"
-				case 'f':
-					recommend += ",flow"
-				default:
-					msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st)
-					panic(externalPanic(msg))
-				}
-			}
-			msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend)
-			panic(externalPanic(msg))
-		}
-
+		inline := false
 		fields := strings.Split(tag, ",")
 		if len(fields) > 1 {
 			for _, flag := range fields[1:] {
@@ -219,6 +209,8 @@ func getStructFields(st reflect.Type) (*structFields, error) {
 					info.OmitEmpty = true
 				case "flow":
 					info.Flow = true
+				case "inline":
+					inline = true
 				default:
 					msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
 					panic(externalPanic(msg))
@@ -227,6 +219,41 @@ func getStructFields(st reflect.Type) (*structFields, error) {
 			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:
+				sfields, err := getStructFields(field.Type)
+				if err != nil {
+					return nil, err
+				}
+				for _, finfo := range sfields.List {
+					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")
+				panic("Option ,inline needs a struct value field")
+			}
+			continue
+		}
+
 		if tag != "" {
 			info.Key = tag
 		} else {
@@ -238,11 +265,11 @@ func getStructFields(st reflect.Type) (*structFields, error) {
 			return nil, errors.New(msg)
 		}
 
-		fieldsList[len(fieldsMap)] = info
+		fieldsList = append(fieldsList, info)
 		fieldsMap[info.Key] = info
 	}
 
-	fields = &structFields{fieldsMap, fieldsList[:len(fieldsMap)]}
+	fields = &structFields{fieldsMap, fieldsList, inlineMap}
 
 	fieldMapMutex.Lock()
 	fieldMap[st] = fields