package yu_proto_old import ( "fmt" "reflect" "strconv" "strings" "sync" ) type TagPrefix int // Possible tag options. const ( TagNone TagPrefix = iota TagOptional TagRequired ) func ParseTag(field reflect.StructField) (id int, opt TagPrefix, name string) { tag := field.Tag.Get("protobuf") if tag == "" { return } parts := strings.Split(tag, ",") for _, part := range parts { if part == "opt" { opt = TagOptional } else if part == "req" { opt = TagRequired } else { i, err := strconv.Atoi(part) if err != nil { name = part } else { id = int(i) } } } return } // ProtoField contains cached reflected metadata for struct fields. type ProtoField struct { ID int64 Prefix TagPrefix Name string // If non-empty, tag-defined field name. Index []int Field reflect.StructField } func (p *ProtoField) Required() bool { return p.Prefix == TagRequired || p.Field.Type.Kind() != reflect.Ptr } var cache = map[reflect.Type][]*ProtoField{} var cacheLock sync.Mutex func ProtoFields(t reflect.Type) []*ProtoField { cacheLock.Lock() idx, ok := cache[t] cacheLock.Unlock() if ok { return idx } id := 0 idx = innerFieldIndexes(&id, t) seen := map[int64]struct{}{} for _, i := range idx { if _, ok := seen[i.ID]; ok { panic(fmt.Sprintf("protobuf ID %d reused in %s.%s", i.ID, t.PkgPath(), t.Name())) } seen[i.ID] = struct{}{} } cacheLock.Lock() defer cacheLock.Unlock() cache[t] = idx return idx } func innerFieldIndexes(id *int, v reflect.Type) []*ProtoField { if v.Kind() == reflect.Ptr { return innerFieldIndexes(id, v.Elem()) } out := []*ProtoField{} for i := 0; i < v.NumField(); i++ { f := v.Field(i) *id++ tid, prefix, name := ParseTag(f) if tid != 0 { *id = tid } if f.Anonymous { *id-- for _, inner := range innerFieldIndexes(id, f.Type) { inner.Index = append([]int{i}, inner.Index...) out = append(out, inner) } } else { out = append(out, &ProtoField{ ID: int64(*id), Prefix: prefix, Name: name, Index: []int{i}, Field: f, }) } } return out }