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() }