github.com/clubpay/ronykit/kit@v0.14.4-0.20240515065620-d0dace45cbc7/utils/reflector/reflector.go (about)

     1  package reflector
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"sync"
     8  	"unicode"
     9  
    10  	"github.com/clubpay/ronykit/kit"
    11  )
    12  
    13  var (
    14  	ErrNotExported        = fmt.Errorf("not exported")
    15  	ErrNoField            = fmt.Errorf("field not exists")
    16  	ErrMessageIsNotStruct = fmt.Errorf("message is not a struct")
    17  )
    18  
    19  type Reflector struct {
    20  	cacheMtx sync.RWMutex
    21  	cache    map[reflect.Type]*Reflected
    22  }
    23  
    24  func New() *Reflector {
    25  	return &Reflector{
    26  		cache: map[reflect.Type]*Reflected{},
    27  	}
    28  }
    29  
    30  // Register registers the message then reflector will be much faster. You should call
    31  // it concurrently.
    32  func Register(m kit.Message, tags ...string) {
    33  	if m == nil {
    34  		return
    35  	}
    36  
    37  	mVal, err := getValue(m)
    38  	if err != nil {
    39  		return
    40  	}
    41  	mType := mVal.Type()
    42  	registered[mType] = destruct(mType, tags...)
    43  }
    44  
    45  func getValue(m kit.Message) (reflect.Value, error) {
    46  	mVal := reflect.Indirect(reflect.ValueOf(m))
    47  	if mVal.Kind() != reflect.Struct {
    48  		return reflect.Value{}, ErrMessageIsNotStruct
    49  	}
    50  
    51  	return mVal, nil
    52  }
    53  
    54  func destruct(mType reflect.Type, tags ...string) *Reflected {
    55  	r := &Reflected{
    56  		obj:   Fields{},
    57  		byTag: map[string]Fields{},
    58  		typ:   mType,
    59  	}
    60  
    61  	for i := 0; i < mType.NumField(); i++ {
    62  		ft := mType.Field(i)
    63  		if ft.PkgPath != "" {
    64  			continue
    65  		}
    66  		fi := FieldInfo{
    67  			idx:    i,
    68  			f:      ft,
    69  			name:   ft.Name,
    70  			offset: ft.Offset,
    71  			typ:    ft.Type,
    72  		}
    73  
    74  		switch ft.Type.Kind() {
    75  		case reflect.Map, reflect.Slice, reflect.Ptr,
    76  			reflect.Interface, reflect.Array, reflect.Chan,
    77  			reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
    78  		default:
    79  			fi.unsafe = true
    80  		}
    81  
    82  		r.obj[fi.name] = fi
    83  		for _, t := range tags {
    84  			v, ok := ft.Tag.Lookup(t)
    85  			if !ok {
    86  				continue
    87  			}
    88  			idx := strings.IndexFunc(v, unicode.IsPunct)
    89  			if idx != -1 {
    90  				v = v[:idx]
    91  			}
    92  			if r.byTag[t] == nil {
    93  				r.byTag[t] = Fields{}
    94  			}
    95  			r.byTag[t][v] = fi
    96  		}
    97  	}
    98  
    99  	return r
   100  }
   101  
   102  func (r *Reflector) Load(m kit.Message, tags ...string) *Reflected {
   103  	mType := reflect.Indirect(reflect.ValueOf(m)).Type()
   104  	cachedData := registered[mType]
   105  	if cachedData == nil {
   106  		r.cacheMtx.RLock()
   107  		cachedData = r.cache[mType]
   108  		r.cacheMtx.RUnlock()
   109  		if cachedData == nil {
   110  			cachedData = destruct(mType, tags...)
   111  			r.cacheMtx.Lock()
   112  			r.cache[mType] = cachedData
   113  			r.cacheMtx.Unlock()
   114  		}
   115  	}
   116  
   117  	return cachedData
   118  }
   119  
   120  func (r *Reflector) Get(m kit.Message, fieldName string) (any, error) {
   121  	e, err := getValue(m)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	f, ok := e.Type().FieldByName(fieldName)
   127  	if !ok {
   128  		return nil, ErrNoField
   129  	}
   130  	if f.PkgPath != "" {
   131  		return nil, ErrNotExported
   132  	}
   133  
   134  	return e.FieldByName(fieldName).Interface(), nil
   135  }
   136  
   137  func (r *Reflector) GetString(m kit.Message, fieldName string) (string, error) {
   138  	e, err := getValue(m)
   139  	if err != nil {
   140  		return "", err
   141  	}
   142  	f, ok := e.Type().FieldByName(fieldName)
   143  	if !ok {
   144  		return "", ErrNoField
   145  	}
   146  	if f.PkgPath != "" {
   147  		return "", ErrNotExported
   148  	}
   149  
   150  	return e.FieldByName(fieldName).String(), nil
   151  }
   152  
   153  func (r *Reflector) GetInt(m kit.Message, fieldName string) (int64, error) {
   154  	e, err := getValue(m)
   155  	if err != nil {
   156  		return 0, err
   157  	}
   158  	f, ok := e.Type().FieldByName(fieldName)
   159  	if !ok {
   160  		return 0, ErrNoField
   161  	}
   162  	if f.PkgPath != "" {
   163  		return 0, ErrNotExported
   164  	}
   165  
   166  	return e.FieldByName(fieldName).Int(), nil
   167  }