diff --git a/packs/rfield.go b/packs/rfield.go index cb96b73..7219ee0 100644 --- a/packs/rfield.go +++ b/packs/rfield.go @@ -28,8 +28,8 @@ import "reflect" type ( RField struct { + RFieldFlags RType reflect.Type - Flags RFieldFlags Name string SName string // scaped name @@ -40,9 +40,22 @@ type ( BName []byte // name bytes Index []int } + + RFields []RField + ) +func (f RFields) Dominant() (RField, bool) { + // The Fields are sorted in increasing index-length order, then by presence of tag. + // That means that the first RField is the dominant one. We need only check + // for error cases: two Fields at top level, either both tagged or neither tagged. + if len(f) > 1 && len(f[0].Index) == len(f[1].Index) && f[0].Tagged() == f[1].Tagged() { + return RField{}, false + } + return f[0], true +} + diff --git a/packs/rfields_type.go b/packs/rfields_type.go index 56ed3cf..2f56883 100644 --- a/packs/rfields_type.go +++ b/packs/rfields_type.go @@ -34,15 +34,15 @@ import ( type ( RTypeFields struct { - RFields []RField - Index map[string]int + Fields RFields + Index map[string]int } ) -func NewRTypeRFields (t reflect.Type) *RTypeFields { - // Anonymous RFields to explore at the current level and the next. - current := []RField{} +func NewRTypeFields (t reflect.Type) *RTypeFields { + // Anonymous Fields to explore at the current level and the next. + current := RFields{} next := []RField{{RType: t}} // Count of queued names for current level and the next. @@ -51,8 +51,8 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { // Types already visited at an earlier level. visited := map[reflect.Type]bool{} - // RFields found. - var RFields []RField + // Fields found. + var fs []RField // Buffer to run HTMLEscape on RField names. var nameEscBuf bytes.Buffer @@ -67,7 +67,7 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { } visited[f.RType] = true - // Scan f.RType for RFields to include. + // Scan f.RType for Fields to include. for i := 0; i < f.RType.NumField(); i++ { var flags RFieldFlags sf := f.RType.Field(i) @@ -77,13 +77,13 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { t = t.Elem() } if !sf.IsExported() && t.Kind() != reflect.Struct { - // Ignore embedded RFields of unexported non-struct types. + // Ignore embedded Fields of unexported non-struct types. continue } - // Do not ignore embedded RFields of unexported struct types - // since they may have exported RFields. + // Do not ignore embedded Fields of unexported struct types + // since they may have exported Fields. } else if !sf.IsExported() { - // Ignore unexported non-embedded RFields. + // Ignore unexported non-embedded Fields. continue } tag := sf.Tag.Get("json") @@ -118,7 +118,9 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { // Record found RField and index sequence. if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { - tagged := name != "" + if name != "" { + flags |= RFieldFlagTagged + } if name == "" { name = sf.Name } @@ -145,13 +147,13 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { rf.HName = nameEscBuf.String() //rf. = `"` + rf.Name + `":` - RFields = append(RFields, rf) + fs = append(fs, rf) if count[f.RType] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. - RFields = append(RFields, RFields[len(RFields)-1]) + fs = append(fs, fs[len(fs)-1]) } continue } @@ -165,8 +167,8 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { } } - sort.Slice(RFields, func(i, j int) bool { - x := RFields + sort.Slice(fs, func(i, j int) bool { + x := fs // sort RField by name, breaking ties with depth, then // breaking ties with "name came from json tag", then // breaking ties with index sequence. @@ -176,26 +178,26 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { if len(x[i].Index) != len(x[j].Index) { return len(x[i].Index) < len(x[j].Index) } - if x[i].Flags.Tagged() != x[j].Flags.Tagged() { - return x[i].Flags.Tagged() + if x[i].Tagged() != x[j].Tagged() { + return x[i].Tagged() } return RFieldsByIndex(x).Less(i, j) }) - // Delete all RFields that are hidden by the Go rules for embedded RFields, - // except that RFields with JSON tags are promoted. + // Delete all Fields that are hidden by the Go rules for embedded Fields, + // except that Fields with JSON tags are promoted. - // The RFields are sorted in primary order of name, secondary order + // The Fields are sorted in primary order of name, secondary order // of RField index length. Loop over names; for each name, delete - // hidden RFields by choosing the one dominant RField that survives. - out := RFields[:0] - for advance, i := 0, 0; i < len(RFields); i += advance { + // hidden Fields by choosing the one dominant RField that survives. + out := fs[:0] + for advance, i := 0, 0; i < len(fs); i += advance { // One iteration per name. - // Find the sequence of RFields with the name of this first RField. - fi := RFields[i] + // Find the sequence of Fields with the name of this first RField. + fi := fs[i] name := fi.Name - for advance = 1; i+advance < len(RFields); advance++ { - fj := RFields[i+advance] + for advance = 1; i+advance < len(fs); advance++ { + fj := fs[i+advance] if fj.Name != name { break } @@ -204,45 +206,31 @@ func NewRTypeRFields (t reflect.Type) *RTypeFields { out = append(out, fi) continue } - dominant, ok := dominantRField(RFields[i : i+advance]) + dominant, ok := RFields(fs[i : i+advance]).Dominant() if ok { out = append(out, dominant) } } - RFields = out - sort.Sort(RFieldsByIndex(RFields)) + fs = out + sort.Sort(RFieldsByIndex(fs)) - for i := range RFields { - //f := &RFields[i] + for i := range fs { + //f := &fs[i] //f.encoder = typeEncoder(typeByIndex(t, f.index)) } - nameIndex := make(map[string]int, len(RFields)) - for i, RField := range RFields { - nameIndex[RField.Name] = i + nameIndex := make(map[string]int, len(fs)) + for i, f := range fs { + nameIndex[f.Name] = i } return &RTypeFields{ - RFields: RFields, - Index : nameIndex, + Fields: fs, + Index : nameIndex, } } -// dominantRField looks through the RFields, all of which are known to -// have the same name, to find the single RField that dominates the -// others using Go's embedding rules, modified by the presence of -// JSON tags. If there are multiple top-level RFields, the boolean -// will be false: This condition is an error in Go and we skip all -// the RFields. -func dominantRField(RFields []RField) (RField, bool) { - // The RFields are sorted in increasing index-length order, then by presence of tag. - // That means that the first RField is the dominant one. We need only check - // for error cases: two RFields at top level, either both tagged or neither tagged. - if len(RFields) > 1 && len(RFields[0].Index) == len(RFields[1].Index) && RFields[0].Tag == Fields[1].Tag { - return RField{}, false - } - return RFields[0], true -} + func parseTag(tag string) (string, tagOptions) {