github.com/mitranim/gg@v0.1.17/reflect.go (about)

     1  package gg
     2  
     3  import (
     4  	"path"
     5  	r "reflect"
     6  	"runtime"
     7  	rt "runtime"
     8  	"strings"
     9  )
    10  
    11  /*
    12  Returns `reflect.Type` of the given type. Differences from `reflect.TypeOf`:
    13  
    14  	* Avoids spurious heap escape and copying.
    15  	* Output is always non-nil.
    16  	* When the given type is an interface, including the empty interface `any`,
    17  	  the output is a non-nil `reflect.Type` describing the given interface,
    18  	  rather than the concrete underlying type.
    19  */
    20  func Type[A any]() r.Type { return r.TypeOf((*A)(nil)).Elem() }
    21  
    22  /*
    23  Similar to `reflect.TypeOf`, with the following differences:
    24  
    25  	* Avoids spurious heap escape and copying.
    26  	* Output is always non-nil.
    27  	* When the given type is an interface, including the empty interface `any`,
    28  	  the output is a non-nil `reflect.Type` describing the given interface.
    29  */
    30  func TypeOf[A any](A) r.Type { return Type[A]() }
    31  
    32  /*
    33  Nil-safe version of `reflect.Type.Kind`. If the input is nil, returns
    34  `reflect.Invalid`.
    35  */
    36  func TypeKind(val r.Type) r.Kind {
    37  	if val == nil {
    38  		return r.Invalid
    39  	}
    40  	return val.Kind()
    41  }
    42  
    43  // Nil-safe version of `reflect.Type.String`. If the input is nil, returns zero.
    44  func TypeString(val r.Type) string {
    45  	if val == nil {
    46  		return ``
    47  	}
    48  	return val.String()
    49  }
    50  
    51  // True if both type parameters are exactly the same.
    52  func EqType[A, B any]() bool { return Type[A]() == Type[B]() }
    53  
    54  // True if both type parameters are not exactly the same.
    55  func NotEqType[A, B any]() bool { return Type[A]() != Type[B]() }
    56  
    57  /*
    58  Returns `reflect.Kind` of the given `any`. Compare our generic functions `Kind`
    59  and `KindOf` which take a concrete type.
    60  */
    61  func KindOfAny(val any) r.Kind {
    62  	return TypeKind(r.TypeOf(AnyNoEscUnsafe(val)))
    63  }
    64  
    65  /*
    66  Returns `reflect.Kind` of the given type. Never returns `reflect.Invalid`. If
    67  the type parameter is an interface, the output is `reflect.Interface`.
    68  */
    69  func Kind[A any]() r.Kind { return Type[A]().Kind() }
    70  
    71  /*
    72  Returns `reflect.Kind` of the given type. Never returns `reflect.Invalid`. If
    73  the type parameter is an interface, the output is `reflect.Interface`.
    74  */
    75  func KindOf[A any](A) r.Kind { return Type[A]().Kind() }
    76  
    77  // Uses `reflect.Zero` to create a zero value of the given type.
    78  func ZeroValue[A any]() r.Value { return r.Zero(Type[A]()) }
    79  
    80  // Takes an arbitrary function and returns its name.
    81  func FuncName(val any) string { return FuncNameBase(RuntimeFunc(val)) }
    82  
    83  // Takes an arbitrary function and returns its `runtime.Func`.
    84  func RuntimeFunc(val any) *rt.Func {
    85  	return runtime.FuncForPC(r.ValueOf(val).Pointer())
    86  }
    87  
    88  // Returns the given function's name without the package path prefix.
    89  func FuncNameBase(fun *rt.Func) string {
    90  	if fun == nil {
    91  		return ``
    92  	}
    93  	return path.Base(fun.Name())
    94  }
    95  
    96  /*
    97  Returns the name of the given function stripped of various namespaces: package
    98  path prefix, package name, type name.
    99  */
   100  func FuncNameShort(name string) string {
   101  	// TODO cleanup.
   102  
   103  	name = path.Base(name)
   104  
   105  	for len(name) > 0 {
   106  		ind := strings.IndexByte(name, '.')
   107  		if ind >= 0 &&
   108  			len(name) > (ind+1) &&
   109  			!(name[ind+1] == '.' || name[ind+1] == ']') &&
   110  			!isFuncNameAnon(name[:ind]) {
   111  			name = name[ind+1:]
   112  			continue
   113  		}
   114  		break
   115  	}
   116  
   117  	return name
   118  }
   119  
   120  // True if the value's underlying type is convertible to `[]byte`.
   121  func IsValueBytes(val r.Value) bool {
   122  	return val.IsValid() && IsTypeBytes(val.Type())
   123  }
   124  
   125  // True if the type is convertible to `[]byte`.
   126  func IsTypeBytes(typ r.Type) bool {
   127  	return (typ != nil) &&
   128  		(typ.Kind() == r.Slice || typ.Kind() == r.Array) &&
   129  		(typ.Elem().Kind() == r.Uint8)
   130  }
   131  
   132  /*
   133  Safer version of `reflect.Value.IsNil`. Doesn't panic if the value is not
   134  nilable.
   135  */
   136  func IsValueNil(val r.Value) bool { return IsValueNilable(val) && val.IsNil() }
   137  
   138  // Shortcut for `IsKindNilable(val.Kind())`.
   139  func IsValueNilable(val r.Value) bool { return IsKindNilable(val.Kind()) }
   140  
   141  // Shortcut for `IsTypeNilable(TypeKind(val))`.
   142  func IsTypeNilable(val r.Type) bool { return IsKindNilable(TypeKind(val)) }
   143  
   144  /*
   145  True if the given `reflect.Kind` describes a kind whose value can be nil.
   146  On any `reflect.Value` matching this, it's safe to call `.IsNil`.
   147  */
   148  func IsKindNilable(val r.Kind) bool {
   149  	switch val {
   150  	case r.Invalid, r.Chan, r.Func, r.Interface, r.Map, r.Pointer, r.Slice:
   151  		return true
   152  	default:
   153  		return false
   154  	}
   155  }
   156  
   157  /*
   158  Same as `reflect.Value.Type`, but when value is invalid, returns nil instead of
   159  panicking.
   160  */
   161  func ValueType(src r.Value) r.Type {
   162  	if src.IsValid() {
   163  		return src.Type()
   164  	}
   165  	return nil
   166  }
   167  
   168  /*
   169  If the underlying type is compatible with `Text`, unwraps and converts it to a
   170  string. Otherwise returns zero value. Boolean indicates success.
   171  */
   172  func AnyToString(src any) (string, bool) {
   173  	switch src := AnyNoEscUnsafe(src).(type) {
   174  	case string:
   175  		return src, true
   176  	case []byte:
   177  		return ToString(src), true
   178  	}
   179  
   180  	return ValueToString(r.ValueOf(AnyNoEscUnsafe(src)))
   181  }
   182  
   183  // Reflection-based component of `AnyToString`. For internal use.
   184  func ValueToString(val r.Value) (string, bool) {
   185  	if !val.IsValid() {
   186  		return ``, true
   187  	}
   188  
   189  	if val.Kind() == r.String {
   190  		return val.String(), true
   191  	}
   192  
   193  	if IsValueBytes(val) {
   194  		return ToString(val.Bytes()), true
   195  	}
   196  
   197  	return ``, false
   198  }
   199  
   200  /*
   201  If the underlying type is compatible with `Text`, unwraps and converts it to the
   202  given text type. Otherwise returns zero value. Boolean indicates success. If
   203  the given value is backed by `string` byt the output type is backed by `[]byte`,
   204  or vice versa, this performs a regular Go conversion, which may allocate.
   205  Otherwise this doesn't allocate.
   206  */
   207  func AnyToText[A Text](src any) (A, bool) {
   208  	return ValueToText[A](r.ValueOf(AnyNoEscUnsafe(src)))
   209  }
   210  
   211  // Reflection-based component of `AnyToText`. For internal use.
   212  func ValueToText[A Text](val r.Value) (A, bool) {
   213  	if !val.IsValid() {
   214  		return Zero[A](), true
   215  	}
   216  
   217  	if val.Kind() == r.String {
   218  		return A(val.String()), true
   219  	}
   220  
   221  	if IsValueBytes(val) {
   222  		return A(val.Bytes()), true
   223  	}
   224  
   225  	return Zero[A](), false
   226  }
   227  
   228  /*
   229  Same as `ValueToString` but instead of boolean true/false, returns a nil/non-nil
   230  error. The error describes the failure to convert the input to a string.
   231  */
   232  func ValueToStringCatch(val r.Value) (string, error) {
   233  	out, ok := ValueToString(val)
   234  	if ok {
   235  		return out, nil
   236  	}
   237  	return out, ErrConv(val.Interface(), Type[string]())
   238  }
   239  
   240  func ValidateKind(tar r.Type, exp r.Kind) {
   241  	if TypeKind(tar) != exp {
   242  		panic(Errf(
   243  			`expected type of kind %q, got type %v of kind %q`,
   244  			exp, tar, TypeKind(tar),
   245  		))
   246  	}
   247  }
   248  
   249  var StructFieldCache = TypeCacheOf[StructFields]()
   250  
   251  type StructFields []r.StructField
   252  
   253  func (self *StructFields) Init(src r.Type) {
   254  	TimesAppend(self, src.NumField(), src.Field)
   255  }
   256  
   257  var StructPublicFieldCache = TypeCacheOf[StructPublicFields]()
   258  
   259  type StructPublicFields []r.StructField
   260  
   261  func (self *StructPublicFields) Init(src r.Type) {
   262  	FilterAppend(self, StructFieldCache.Get(src), IsFieldPublic)
   263  }
   264  
   265  /*
   266  For any given struct type, returns a slice of its fields including fields of
   267  embedded structs. Structs embedded by value (not by pointer) are considered
   268  parts of the enclosing struct, rather than fields in their own right, and their
   269  fields are included into this function's output. This is NOT equivalent to the
   270  fields you would get by iterating over `reflect.Type.NumField`. Not only
   271  because it includes the fields of value-embedded structs, but also because it
   272  adjusts `reflect.StructField.Index` and `reflect.StructField.Offset`
   273  specifically for the given ancestor type. In particular,
   274  `reflect.StructField.Offset` of deeply-nested fields is exactly equivalent to
   275  the output of `unsafe.Offsetof` for the same parent type and field, which is
   276  NOT what you would normally get from the "reflect" package.
   277  
   278  For comparison. Normally when using `reflect.Type.FieldByIndex`, the returned
   279  fields have both their offset and their index relative to their most immediate
   280  parent, rather than the given ancestor. But it's also inconsistent. When using
   281  `reflect.Type.FieldByName`, the returned fields have their index relative to
   282  the ancestor, but their offset is still relative to their most immediate
   283  parent.
   284  
   285  This implementation fixes ALL of that. It gives you fields where offsets AND
   286  indexes are all relative to the ancestor.
   287  
   288  Caches and reuses the resulting slice for all future calls for any given type.
   289  The resulting slice, its elements, or any inner slices, must not be mutated.
   290  */
   291  var StructDeepPublicFieldCache = TypeCacheOf[StructDeepPublicFields]()
   292  
   293  // Used by `StructDeepPublicFieldCache`.
   294  type StructDeepPublicFields []r.StructField
   295  
   296  // Implement an interface used by `TypeCache`.
   297  func (self *StructDeepPublicFields) Init(src r.Type) {
   298  	ValidateKind(src, r.Struct)
   299  	path := make([]int, 0, expectedStructNesting)
   300  	self.append(&path, r.StructField{Type: src, Anonymous: true})
   301  }
   302  
   303  func (self *StructDeepPublicFields) append(path *[]int, field r.StructField) {
   304  	defer SnapSlice(path).Done()
   305  	Append(path, field.Index...)
   306  
   307  	if IsFieldEmbed(field) {
   308  		for _, inner := range StructPublicFieldCache.Get(field.Type) {
   309  			inner.Offset += field.Offset
   310  			self.append(path, inner)
   311  		}
   312  		return
   313  	}
   314  
   315  	field.Index = Clone(*path)
   316  	Append(self, field)
   317  }
   318  
   319  var JsonNameToDbNameCache = TypeCacheOf[JsonNameToDbName]()
   320  
   321  type JsonNameToDbName map[string]string
   322  
   323  func (self *JsonNameToDbName) Init(src r.Type) {
   324  	for _, field := range StructDeepPublicFieldCache.Get(src) {
   325  		MapSetOpt(MapInit(self), FieldJsonName(field), FieldDbName(field))
   326  	}
   327  }
   328  
   329  var DbNameToJsonNameCache = TypeCacheOf[DbNameToJsonName]()
   330  
   331  type DbNameToJsonName map[string]string
   332  
   333  func (self *DbNameToJsonName) Init(src r.Type) {
   334  	for _, field := range StructDeepPublicFieldCache.Get(src) {
   335  		MapSetOpt(MapInit(self), FieldDbName(field), FieldJsonName(field))
   336  	}
   337  }
   338  
   339  var JsonNameToDbFieldCache = TypeCacheOf[JsonNameToDbField]()
   340  
   341  type JsonNameToDbField map[string]r.StructField
   342  
   343  func (self *JsonNameToDbField) Init(src r.Type) {
   344  	for _, field := range StructDeepPublicFieldCache.Get(src) {
   345  		if IsNotZero(FieldDbName(field)) {
   346  			MapSetOpt(MapInit(self), FieldJsonName(field), field)
   347  		}
   348  	}
   349  }
   350  
   351  var DbNameToJsonFieldCache = TypeCacheOf[DbNameToJsonField]()
   352  
   353  type DbNameToJsonField map[string]r.StructField
   354  
   355  func (self *DbNameToJsonField) Init(src r.Type) {
   356  	for _, field := range StructDeepPublicFieldCache.Get(src) {
   357  		if IsNotZero(FieldJsonName(field)) {
   358  			MapSetOpt(MapInit(self), FieldDbName(field), field)
   359  		}
   360  	}
   361  }
   362  
   363  /*
   364  Takes a struct field tag and returns its identifier part, following the
   365  "encoding/json" conventions. Ident "-" is converted to "". Usage:
   366  
   367  	ident := TagIdent(someField.Tag.Get(`json`))
   368  	ident := TagIdent(someField.Tag.Get(`db`))
   369  
   370  Rules:
   371  
   372  	json:"ident"         -> "ident"
   373  	json:"ident,<extra>" -> "ident"
   374  	json:"-"             -> ""
   375  	json:"-,<extra>"     -> ""
   376  */
   377  func TagIdent(val string) string {
   378  	ind := strings.IndexRune(string(val), ',')
   379  	if ind >= 0 {
   380  		val = val[:ind]
   381  	}
   382  	if val == `-` {
   383  		return ``
   384  	}
   385  	return val
   386  }
   387  
   388  /*
   389  Returns the field's DB/SQL column name from the "db" tag, following the same
   390  conventions as the `encoding/json` package.
   391  */
   392  func FieldDbName(val r.StructField) string {
   393  	return TagIdent(val.Tag.Get(`db`))
   394  }
   395  
   396  /*
   397  Returns the field's JSON column name from the "json" tag, following the same
   398  conventions as the `encoding/json` package.
   399  */
   400  func FieldJsonName(val r.StructField) string {
   401  	return TagIdent(val.Tag.Get(`json`))
   402  }
   403  
   404  /*
   405  Self-explanatory. For some reason this is not provided in usable form by
   406  the "reflect" package.
   407  */
   408  func IsFieldPublic(val r.StructField) bool { return val.PkgPath == `` }
   409  
   410  /*
   411  True if the given field represents an embedded non-pointer struct type.
   412  False if not embedded or embedded by pointer.
   413  */
   414  func IsFieldEmbed(val r.StructField) bool {
   415  	return val.Anonymous && TypeKind(val.Type) == r.Struct
   416  }
   417  
   418  /*
   419  Dereferences the given type, converting `reflect.Pointer` to its element type as
   420  many times as necessary. Returns an underlying non-pointer type.
   421  */
   422  func TypeDeref(val r.Type) r.Type {
   423  	for val != nil && val.Kind() == r.Pointer {
   424  		val = val.Elem()
   425  	}
   426  	return val
   427  }
   428  
   429  /*
   430  Dereferences the given value until it's no longer a pointer. If the input is a
   431  nil pointer, or if any intermediary pointers are nil, returns an empty/invalid
   432  value. Also see `ValueDerefAlloc` which allocates intermediary pointers as
   433  necessary/possible.
   434  */
   435  func ValueDeref(val r.Value) r.Value {
   436  	for val.Kind() == r.Pointer {
   437  		if val.IsNil() {
   438  			return r.Value{}
   439  		}
   440  		val = val.Elem()
   441  	}
   442  	return val
   443  }
   444  
   445  /*
   446  Dereferences the given value until it's no longer a pointer, allocating
   447  intermediary pointers as necessary/possible. Also see `ValueDerefAlloc` which
   448  does not allocate intermediaries.
   449  */
   450  func ValueDerefAlloc(val r.Value) r.Value {
   451  	for val.Kind() == r.Pointer {
   452  		if val.IsNil() {
   453  			if !val.CanSet() {
   454  				return r.Value{}
   455  			}
   456  			val.Set(r.New(val.Type().Elem()))
   457  		}
   458  		val = val.Elem()
   459  	}
   460  	return val
   461  }
   462  
   463  /*
   464  True if the given type may contain any indirections (pointers). For any "direct"
   465  type, assigning a value to another variable via `A := B` makes a complete copy.
   466  For any "indirect" type, reassignment is insufficient to make a copy.
   467  
   468  Special exceptions:
   469  
   470  	* Strings are considered to be direct, despite containing a pointer.
   471  	  Generally in Go, strings are considered to be immutable.
   472  	* Chans are ignored / considered to be direct.
   473  	* Funcs are ignored / considered to be direct.
   474  	* For structs, only public fields are checked.
   475  */
   476  func IsIndirect(typ r.Type) bool {
   477  	switch TypeKind(typ) {
   478  	case r.Array:
   479  		return typ.Len() > 0 && IsIndirect(typ.Elem())
   480  	case r.Slice:
   481  		return true
   482  	case r.Interface:
   483  		return true
   484  	case r.Map:
   485  		return true
   486  	case r.Pointer:
   487  		return true
   488  	case r.Struct:
   489  		return Some(StructPublicFieldCache.Get(typ), IsFieldIndirect)
   490  	default:
   491  		return false
   492  	}
   493  }
   494  
   495  // Shortcut for testing if the field's type is `IsIndirect`.
   496  func IsFieldIndirect(val r.StructField) bool { return IsIndirect(val.Type) }
   497  
   498  /*
   499  Returns a deep clone of the given value. Doesn't clone chans and funcs,
   500  preserving them as-is. If the given value is "direct" (see `IsIndirect`),
   501  this function doesn't allocate and simply returns the input as-is.
   502  */
   503  func CloneDeep[A any](val A) A {
   504  	ValueClone(r.ValueOf(AnyNoEscUnsafe(&val)).Elem())
   505  	return val
   506  }
   507  
   508  /*
   509  Replaces the given value, which must be settable, with a deep clone, if the
   510  value is indirect. See `IsIndirect`.
   511  */
   512  func ValueClone(src r.Value) {
   513  	switch src.Kind() {
   514  	case r.Array:
   515  		cloneArray(src)
   516  	case r.Slice:
   517  		cloneSlice(src)
   518  	case r.Interface:
   519  		cloneInterface(src)
   520  	case r.Map:
   521  		cloneMap(src)
   522  	case r.Pointer:
   523  		clonePointer(src)
   524  	case r.Struct:
   525  		cloneStruct(src)
   526  	}
   527  }
   528  
   529  // Similar to `CloneDeep` but takes and returns `reflect.Value`.
   530  func ValueCloned(src r.Value) r.Value {
   531  	switch src.Kind() {
   532  	case r.Array:
   533  		return clonedArray(src)
   534  	case r.Slice:
   535  		return clonedSlice(src)
   536  	case r.Interface:
   537  		return clonedInterface(src)
   538  	case r.Map:
   539  		return clonedMap(src)
   540  	case r.Pointer:
   541  		return clonedPointer(src)
   542  	case r.Struct:
   543  		return clonedStruct(src)
   544  	default:
   545  		return src
   546  	}
   547  }
   548  
   549  // Idempotent set. Calls `reflect.Value.Set` only if the inputs are distinct.
   550  func ValueSet(tar, src r.Value) {
   551  	if tar != src {
   552  		tar.Set(src)
   553  	}
   554  }
   555  
   556  // Shortcut for `reflect.New(typ).Elem()`.
   557  func NewElem(typ r.Type) r.Value { return r.New(typ).Elem() }