rf

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2021 License: Unlicense Imports: 5 Imported by: 0

README

Overview

Short for "reflect utils": utilities missing from the "reflect" package in the Go standard library. Small and dependency-free.

See the full documentation at https://pkg.go.dev/github.com/mitranim/rf.

Changelog

v0.2.0

Complete revision.

  • Removed useless or rarely-used utils.
  • Added many missing utils.
  • New approach to walking / traversal. The old naive approach walked the entire structure every time. The new approach is to JIT-compile a precise walker that visits just what you need, caching it for a combination of type + filter. This makes walking dramatically more efficient.
  • Added Cache for generating and caching arbitrary type-dependent structures.
  • Renamed from github.com/mitranim/refut to github.com/mitranim/rf for brevity.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CopyPath

func CopyPath(src []int) []int

Shortcut for copying struct field paths, suitable for `reflect.StructField.Index`. Such copying is required when walking a struct type to build a collection of fields for later use (such representations should also be cached via `rf.Cache`).

func DerefFunc

func DerefFunc(val interface{}) r.Value

Shortcut for `rf.DerefValueWithKind(val, reflect.Func)`.

func DerefKind

func DerefKind(val interface{}) r.Kind

Shortcut for `rf.TypeKind(rf.DerefType(val))`. Returns the kind of the provided value, automatically dereferencing any outer pointer types until it finds a non-pointer type. If the input is nil, returns `reflect.Invalid`.

func DerefLen

func DerefLen(val interface{}) int

Shortcut for `rf.ValueLen(rf.DerefValue(val))`. Returns the length of the given value, if possible. If not, returns 0. Automatically dereferences the input.

func DerefMap

func DerefMap(val interface{}) r.Value

Shortcut for `rf.DerefValueWithKind(val, reflect.Map)`.

func DerefPtr

func DerefPtr(val interface{}) r.Value

Shortcut for `rf.DerefValueWithKind(val, reflect.Ptr)`.

func DerefSlice

func DerefSlice(val interface{}) r.Value

Shortcut for `rf.DerefValueWithKind(val, reflect.Slice)`.

func DerefStruct

func DerefStruct(val interface{}) r.Value

Shortcut for `rf.DerefValueWithKind(val, reflect.Struct)`.

func DerefType

func DerefType(val interface{}) r.Type

Same as `reflect.TypeOf`, but dereferences the type, allowing to pass a nil pointer, which doesn't allocate. The following expressions both produce the type of `time.Time`, but the second one is cheaper:

rf.DerefType(time.Time{})       // Allocates.
rf.DerefType((*time.Time)(nil)) // Doesn't allocate.

func DerefValue

func DerefValue(val interface{}) r.Value

Shortcut for `rf.ValueDeref(reflect.ValueOf(val))`. Returns the `reflect.Value` of the input, automatically dereferencing any outer pointers. If any outer pointers are nil, returns `reflect.Value{}`.

func DerefValueWithKind

func DerefValueWithKind(src interface{}, kind r.Kind) r.Value

Ensures that the given value either directly or indirectly (through any number of arbitrarily-nested pointer types) contains a type of the provided kind, and returns its dereferenced value. If any intermediary pointer is nil, the returned value is invalid.

func ElemType

func ElemType(val interface{}) r.Type

Shortcut for `rf.TypeElem(reflect.TypeOf(val))`. Returns the element type of the given value, automatically dereferencing pointer AND slice types, until it finds the first non-pointer, non-slice type. If the input is nil, returns nil.

func FuncName

func FuncName(val interface{}) string

Returns the name of a given function, if possible. Returns "" for nil.

func IsColl

func IsColl(val interface{}) bool

True if the kind of the provided value is a collection. See `rf.IsKindColl` for further details. Note that this follows `reflect.Value.Len` rather than the built-in `len` in that pointers to arrays are not considered to be collections.

func IsEmbed

func IsEmbed(val r.StructField) bool

True if the given field represents an embedded non-pointer struct type. False if it's embedded by pointer.

func IsEmptyColl

func IsEmptyColl(val interface{}) bool

True if the input belongs to one of the kinds "reflect" considers collections (see `rf.IsKindColl`), and has the length of zero. If the input is a non-nil interface that describes a nil collection, such as `([]string)(nil)`, this returns true. If the input is a nil interface, this returns false. Doesn't automatically dereference the input.

func IsKindColl

func IsKindColl(kind r.Kind) bool

True for the kinds "reflect" considers collections: array, chan, map, slice, or string. These are the kinds for which it's safe to call `reflect.Value.Len` without panicking.

func IsKindNilable

func IsKindNilable(kind r.Kind) bool

True for kinds on which it's safe to call `reflect.Value.IsNil`.

func IsNil

func IsNil(val interface{}) bool

True if the input is a nil interface or a non-nil interface containing a nil value. See `rf.IsValueNil`.

func IsPublic

func IsPublic(pkgPath string) bool

True if the input string is empty. Allows to clarify reflection code that checks the public/private status of struct fields and type methods. Examples (oversimplified):

rf.IsPublic(reflect.StructField{}.PkgPath)
rf.IsPublic(reflect.Method{}.PkgPath)

func IsValueEmptyColl

func IsValueEmptyColl(val r.Value) bool

Variant of `rf.IsEmpty` that takes a `reflect.Value` as input. See `rf.IsEmpty` for the documentation.

func IsValueNil

func IsValueNil(val r.Value) bool

True if the input is invalid or contains a nilable value that is nil. Automatically dereferences pointers, returning true for a non-nil pointer to a nil value.

func IsValueZero

func IsValueZero(val r.Value) bool

True if the input is nil (as defined by `rf.IsValueNil`) or a zero value of its type. Automatically dereferences pointers.

func IsZero

func IsZero(val interface{}) bool

Shortcut for `rf.IsValueZero(reflect.ValueOf(val))`. True if the input is nil or a zero value of its type. Automatically dereferences the input; an arbitrarily-nested non-nil pointer to a zero value is considered zero.

func Iter

func Iter(count int) []struct{}

Zero-cost integer iterator. Usage:

for range rf.Iter(N) { ... }
for i := range rf.Iter(N) { ... }

Because `struct{}` is zero-sized, `[]struct{}` is backed by "zerobase" (see "runtime/malloc.go") and does not allocate. This should perform about as well as a "normal" counted loop.

This is not reflection related, but exported because it's used internally a lot.

func Kind

func Kind(val interface{}) r.Kind

Same as `reflect.TypeOf(val).Kind()`, but returns `reflect.Invalid` for a nil input, instead of panicking. Doesn't automatically dereference the input.

func NormNil

func NormNil(val interface{}) interface{}

Short for "normalize nil". If the input is a non-nil interface whose underlying value is nil, this normalizes it, returning a nil interface. Otherwise this returns the input as-is.

func SliceType

func SliceType(val interface{}) r.Type

Shortcut for `reflect.SliceOf(DerefType(val))`. The following expressions are all equivalent:

reflect.SliceOf(reflect.TypeOf(``))
reflect.SliceOf(reflect.TypeOf((*string)(nil)).Elem())
rf.SliceType(``)
rf.SliceType((*string)(nil))

func TagIdent

func TagIdent(tag string) string

Takes a struct field tag and returns its identifier part, following the "encoding/json" conventions. Ident "-" is converted to "". Usage:

ident := rf.TagIdent(someField.Tag.Get(`json`))
ident := rf.TagIdent(someField.Tag.Get(`db`))

Rules:

json:"ident"         -> "ident"
json:"ident,<extra>" -> "ident"
json:"-"             -> ""
json:"-,<extra>"     -> ""

func Trawl

func Trawl(src, out interface{})

Shortcut for `rf.TrawlWith` without an additional filter. Takes an arbitrary source value and a pointer to an output slice. Walks the source value, appending all non-zero values of the matching type to the given slice.

func TrawlWith

func TrawlWith(src, out interface{}, fil Filter)

Shortcut for using `rf.Appender` and `rf.Walk` to trawl the provided "source" value to collect all non-zero values of a specific type into an "output" slice. The source value may be of arbitrary type. The output must be a non-nil pointer to a slice. The additional filter is optional.

func TypeDeref

func TypeDeref(typ r.Type) r.Type

Returns the element type of the provided type, automatically dereferencing pointer types. If the input is nil, returns nil.

func TypeElem

func TypeElem(typ r.Type) r.Type

Returns the element type of the provided type, automatically dereferencing pointer AND slice types, until it finds the first non-pointer, non-slice type. If the input is nil, returns nil.

func TypeKind

func TypeKind(val r.Type) r.Kind

Same as `reflect.Type.Kind(val)`, but returns `reflect.Invalid` for a nil input, instead of panicking. Doesn't automatically dereference the input.

func ValidFunc

func ValidFunc(val interface{}) r.Value

Shortcut for `rf.ValidateFunc(reflect.ValueOf(val))`.

func ValidMap

func ValidMap(val interface{}) r.Value

Shortcut for `rf.ValidateMap(reflect.ValueOf(val))`.

func ValidPtr

func ValidPtr(val interface{}) r.Value

Shortcut for `rf.ValidatePtr(reflect.ValueOf(val))`.

func ValidPtrToKind

func ValidPtrToKind(val interface{}, exp r.Kind) r.Value

Shortcut for `rf.ValidatePtrToKind(reflect.ValueOf(val))`. Converts the input to `reflect.Value`, ensures that it's a non-nil pointer where the inner type has the required kind, and returns the resulting `reflect.Value`.

func ValidSlice

func ValidSlice(val interface{}) r.Value

Shortcut for `rf.ValidateSlice(reflect.ValueOf(val))`.

func ValidSliceOf

func ValidSliceOf(val interface{}, exp r.Type) r.Value

Shortcut for `rf.ValidateSliceOf(reflect.ValueOf(val))`. Converts the input to `reflect.Value`, ensures that it's a slice with the required element type, and returns the resulting `reflect.Value`.

func ValidSliceOfKind

func ValidSliceOfKind(val interface{}, exp r.Kind) r.Value

Shortcut for `rf.ValidateSliceOfKind(reflect.ValueOf(val))`. Converts the input to `reflect.Value`, ensures that it's a slice where the element type has the required kind, and returns the resulting `reflect.Value`.

func ValidStruct

func ValidStruct(val interface{}) r.Value

Shortcut for `rf.ValidateStruct(reflect.ValueOf(val))`.

func ValidTypeFunc

func ValidTypeFunc(val interface{}) r.Type

Shortcut for `rf.ValidateTypeFunc(reflect.TypeOf(val))`.

func ValidTypeMap

func ValidTypeMap(val interface{}) r.Type

Shortcut for `rf.ValidateTypeMap(reflect.TypeOf(val))`.

func ValidTypePtr

func ValidTypePtr(val interface{}) r.Type

Shortcut for `rf.ValidateTypePtr(reflect.TypeOf(val))`.

func ValidTypeSlice

func ValidTypeSlice(val interface{}) r.Type

Shortcut for `rf.ValidateTypeSlice(reflect.TypeOf(val))`.

func ValidTypeStruct

func ValidTypeStruct(val interface{}) r.Type

Shortcut for `rf.ValidateTypeStruct(reflect.TypeOf(val))`.

func ValidateFunc

func ValidateFunc(val r.Value) r.Value

Shortcut for `rf.ValidateValueKind(val, reflect.Func)`.

func ValidateFuncIn

func ValidateFuncIn(typ r.Type, params ...r.Type)

Takes a func type and ensures that its input parameters exactly match the count and types provided to this function, or panics with a descriptive error. Among the provided parameter types, nil serves as a wildcard that matches any type.

func ValidateFuncNumIn

func ValidateFuncNumIn(typ r.Type, exp int)

Takes a func type and ensures that it has the required count of input parameters, or panics with a descriptive error.

func ValidateFuncNumOut

func ValidateFuncNumOut(typ r.Type, exp int)

Takes a func type and ensures that it has the required count of output parameters, or panics with a descriptive error.

func ValidateFuncOut

func ValidateFuncOut(typ r.Type, params ...r.Type)

Takes a func type and ensures that its return parameters exactly match the count and types provided to this function, or panics with a descriptive error. Among the provided parameter types, nil serves as a wildcard that matches any type.

func ValidateMap

func ValidateMap(val r.Value) r.Value

Shortcut for `rf.ValidateValueKind(val, reflect.Map)`.

func ValidatePtr

func ValidatePtr(val r.Value) r.Value

Similar to `rf.ValidateValueKind(val, reflect.Ptr)`, but also ensures that the pointer is non-nil.

func ValidatePtrToKind

func ValidatePtrToKind(val r.Value, exp r.Kind) r.Value

Ensures that the value is a non-nil pointer where the underlying type has the required kind, or panics with a descriptive error. Returns the same value, allowing shorter code. Supports pointers of any depth: `*T`, `**T`, etc. Known limitation: only the outermost pointer is required to be non-nil. Inner pointers may be nil.

func ValidateSlice

func ValidateSlice(val r.Value) r.Value

Shortcut for `rf.ValidateValueKind(val, reflect.Slice)`.

func ValidateSliceOf

func ValidateSliceOf(val r.Value, exp r.Type) r.Value

Ensures that the value is a slice where the element type has the required type, or panics with a descriptive error. Returns the same value, allowing shorter code. Doesn't automatically dereference the input.

func ValidateSliceOfKind

func ValidateSliceOfKind(val r.Value, exp r.Kind) r.Value

Ensures that the value is a slice where the element type has the required kind, or panics with a descriptive error. Returns the same value, allowing shorter code. Doesn't automatically dereference the input.

func ValidateStruct

func ValidateStruct(val r.Value) r.Value

Shortcut for `rf.ValidateValueKind(val, reflect.Struct)`.

func ValidateTypeFunc

func ValidateTypeFunc(typ r.Type) r.Type

Shortcut for `rf.ValidateTypeKind(typ, reflect.Func)`.

func ValidateTypeKind

func ValidateTypeKind(typ r.Type, exp r.Kind) r.Type

Ensures that the type has the required kind or panics with a descriptive error. Returns the same type, allowing shorter code.

func ValidateTypeMap

func ValidateTypeMap(typ r.Type) r.Type

Shortcut for `rf.ValidateTypeKind(typ, reflect.Map)`.

func ValidateTypePtr

func ValidateTypePtr(typ r.Type) r.Type

Shortcut for `rf.ValidateTypeKind(typ, reflect.Ptr)`.

func ValidateTypeSlice

func ValidateTypeSlice(typ r.Type) r.Type

Shortcut for `rf.ValidateTypeKind(typ, reflect.Slice)`.

func ValidateTypeStruct

func ValidateTypeStruct(typ r.Type) r.Type

Shortcut for `rf.ValidateTypeKind(typ, reflect.Struct)`.

func ValidateValueKind

func ValidateValueKind(val r.Value, exp r.Kind) r.Value

Ensures that the value has the required kind or panics with a descriptive error. Returns the same value, allowing shorter code.

func ValueDeref

func ValueDeref(val r.Value) r.Value

Dereferences the provided value until it's no longer a pointer. If the input is a nil pointer or a pointer to a nil pointer (recursively), this returns `reflect.Value{}`.

func ValueLen

func ValueLen(val r.Value) int

If the provided value is a collection via `rf.IsColl`, returns the result of calling `reflect.Value.Len`. Otherwise returns 0. Doesn't automatically dereference the input.

func ValueType

func ValueType(val r.Value) r.Type

Same as `reflect.Value.Type` but "safe" to call on invalid value, in which case this will return nil. Should be used only when nil output type is acceptable.

func Walk

func Walk(val r.Value, fil Filter, vis Visitor)

Takes an arbitrary value and performs deep traversal, invoking the visitor for each node allowed by the filter. Internally, uses `rf.GetWalker` to get or create a walker specialized for this combination of type and filter. For each type+filter combination, `rf.GetWalker` generates a specialized walker, caching it for future calls. This approach allows MUCH more efficient walking.

func WalkFunc

func WalkFunc(val r.Value, fil Filter, vis VisitorFunc)

Shortcut for calling `rf.Walk` with a visitor func.

func Zero

func Zero(ptr interface{})

Zeroes the destination of the given pointer, if possible. The input must be one of:

  • nil interface (nop)
  • nil pointer (nop)
  • non-nil pointer (gets zeroed)

Types

type And

type And [8]Filter

Implementation of `rf.Filter` that combines other filters, allowing to visit nodes for which all non-nil filters return true, and at least one filter is non-nil. If all filters are nil, this returns false.

func (And) ShouldVisit

func (self And) ShouldVisit(typ r.Type, field r.StructField) bool

Implement `rf.Filter`.

type Appender

type Appender [1]r.Value

Implementation of `rf.Visitor` for collecting non-zero values of a single type into a slice. The inner value must be `reflect.Value` holding a slice. The value must be settable. Use `rf.AppenderFor` to instantiate this correctly.

func AppenderFor

func AppenderFor(typ interface{}) Appender

Shortcut for making `rf.Appender`. The input must be a carrier of the element type, not the slice type.

func (Appender) Filter

func (self Appender) Filter() TypeFilter

Returns `rf.TypeFilter` that allows to visit only values suitable to be elements of the slice held by the appender.

func (Appender) Interface

func (self Appender) Interface() interface{}

Shortcut for `self[0].Interface()` insulating the caller from implementation details.

func (Appender) Visit

func (self Appender) Visit(val r.Value, _ r.StructField)

Implement `rf.Visitor` by appending the input value to the inner slice, if the value is non-zero.

type Cache

type Cache struct {
	sync.Map
	Func func(r.Type) interface{}
}

Tiny shortcut for caching arbitrary structures keyed by `reflect.Type`. Any reflection-based code that involves walking arbitrary structs should use `rf.Cache` to "compile" a specialized structure optimized for that particular traversal, and reuse it for subsequent invocations.

func (*Cache) Get

func (self *Cache) Get(key r.Type) interface{}

Gets or creates the structure keyed by the type and generated by `self.Func`, which must be provided at construction time.

type Err

type Err struct {
	While string
	Cause error
}

All errors generated by this package have this type.

func (Err) Error

func (self Err) Error() string

Implement the `error` interface.

func (Err) Unwrap

func (self Err) Unwrap() error

Implement a hidden interface in "errors".

type False

type False struct{}

Implementation of `rf.Filter` that always returns false. Useless because this is the default output for a nil filter. Provided only for symmetry with `rf.True`.

func (False) ShouldVisit

func (False) ShouldVisit(r.Type, r.StructField) bool

Implement `rf.Filter`.

type Filter

type Filter interface {
	ShouldVisit(r.Type, r.StructField) bool
}

Tool for implementing efficient reflect-based deep walking. Determines if a particular node should be visited during a walk. This package provides several filter implementations, such as filtering by type, by struct tag, or combining other filters.

For technical reasons, all implementations of this interface must be values rather than references. For example, filters provided by this package must be used as values rather than pointers. The following is the CORRECT way to construct filters:

var filter rf.Filter = rf.And{
	rf.TypeFilter{rf.DerefType((*string)(nil))},
	rf.TagFilter{`json`, `fieldName`},
}

The following is the INCORRECT way to construct filters. Due to internal validation, this will cause panics at runtime:

var filter rf.Filter = &rf.And{
	&rf.TypeFilter{rf.DerefType((*string)(nil))},
	&rf.TagFilter{`json`, `fieldName`},
}

See also:

rf.Walker
rf.Visitor
rf.GetWalker
rf.Walk

type Nop

type Nop struct{}

No-op implementation of both `rf.Filter` and `rf.Visitor` that doesn't visit anything and does nothing upon visit.

func (Nop) ShouldVisit

func (Nop) ShouldVisit(r.Type, r.StructField) bool

Implement `rf.Filter`.

func (Nop) Visit

func (Nop) Visit(r.Value, r.StructField)

Implement `rf.Visitor`.

type Not

type Not [1]Filter

Implementation of `rf.Filter` that inverts the filter provided to it.

func (Not) ShouldVisit

func (self Not) ShouldVisit(typ r.Type, field r.StructField) bool

Implement `rf.Filter`.

type Or

type Or [8]Filter

Implementation of `rf.Filter` that combines other filters, allowing to visit nodes for which at least one of the filters returns true.

func (Or) ShouldVisit

func (self Or) ShouldVisit(typ r.Type, field r.StructField) bool

Implement `rf.Filter`.

type TagFilter

type TagFilter [2]string

Implementation of `rf.Filter` that allows to visit only values with a specific value of a specific struct field tag.

func (TagFilter) ShouldVisit

func (self TagFilter) ShouldVisit(_ r.Type, field r.StructField) bool

Implement `rf.Filter`.

type True

type True struct{}

Implementation of `rf.Filter` that always returns true.

func (True) ShouldVisit

func (True) ShouldVisit(r.Type, r.StructField) bool

Implement `rf.Filter`.

type TypeFilter

type TypeFilter [1]r.Type

Implementation of `rf.Filter` that allows to visit only values of this specific type.

func (TypeFilter) ShouldVisit

func (self TypeFilter) ShouldVisit(typ r.Type, _ r.StructField) bool

Implement `rf.Filter`.

type Visitor

type Visitor interface {
	Visit(r.Value, r.StructField)
}

Used by `rf.Walker` and `rf.Walk` to visit certain nodes of the given value. A visitor can be an arbitrary value or a function; see `rf.VisitorFunc`.

type VisitorFunc

type VisitorFunc func(r.Value, r.StructField)

Function type that implements `rf.Visitor`. Used by `rf.WalkFunc`. Converting a func to an interface value is alloc-free.

func (VisitorFunc) Visit

func (self VisitorFunc) Visit(val r.Value, field r.StructField)

Implement `rf.Visitor` by calling itself.

type Walker

type Walker interface {
	Walk(r.Value, Visitor)
}

Tool for implementing efficient reflect-based deep walking. The function `rf.GetWalker` generates a walker for a SPECIFIC combination of parent type and `rf.Filter`. The resulting walker is specialized for that combination, and walks its input precisely and efficiently.

For simplicity and efficiency reasons, walkers generated by this package don't additionally assert that the provided `reflect.Value` has the same type for which the walker is generated. When using `rf.Walk` or `rf.WalkFunc`, this is handled for you. Otherwise, it's your responsibility to pass a value of the same type. Walkers also assume that the visitor is non-nil.

func GetWalker

func GetWalker(typ r.Type, fil Filter) Walker

Returns an `rf.Walker` for the given type with the given filter. Uses caching to avoid generating a walker more than once. Future calls with the same inputs will return the same walker instance. Returns nil if for this combination of type and filter, nothing will be visited. A nil filter is equivalent to a filter that always returns false, resulting in a nil walker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL