123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- 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
- }
|