rf

package module
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2022 License: Unlicense Imports: 6 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.5.2

Walking now avoids stack overflow on cyclic types. More specifically, it avoids infinite recursion when generating walkers for cyclic types. Note that fully walking cyclic types is not yet supported; instead, inner occurrences of a cyclic type are ignored, and only the outermost occurrence is walked. This limitation may be lifted in future versions.

v0.5.1

Walking now ignores private fields.

v0.5.0

Converted the following tools to generics, for better type safety and efficiency:

  • TypeFilter
  • IfaceFilter
  • ShallowIfaceFilter
  • Appender
  • Trawl
  • TrawlWith

Added KindFilter (untested).

Added Type.

v0.4.2

Rename WalkFuncPtrWalkPtrFunc.

v0.4.1

Add WalkPtr and WalkFuncPtr. Remove DerefPtr because it made no sense.

v0.4.0

Added:

  • ValueAddr
  • Interface
  • DeepFields
  • TypeDeepFields
  • OffsetFields
  • TypeOffsetFields
  • Path
  • Rev

Breaking changes:

  • Renamed all functions starting with DerefValue* to begin with Deref*.
  • Replaced CopyPath with Path.Copy.

DeepFields and TypeDeepFields is a particularly useful addition, as it supports "flattening" structs, simplifying most use cases that involve struct iteration.

v0.3.3

Add IfaceFilterFor, ShallowIfaceFilterFor.

v0.3.2

Reverted breaking change in v0.3.1: IfaceFilter once again allows to visit descendants. Added ShallowIfaceFilter that doesn't visit descendants of a matching node.

v0.3.1

Quick breaking change: IfaceFilter visits either self or descendants, not both.

v0.3.0

More flexible Filter interface:

  • Previous approach: filter returns bool answering "should visit this node". 2 possible states. Implicitly walks descendants.

  • New approach: filter returns flagset where "should visit this node" and "should walk descendants" are both optional flags. 4 possible states. Walking descendants is now optional.

Renamed Filter.ShouldVisit to Filter.Visit because it's no longer a boolean. This makes it impossible to implement Filter and Walker on the same type, which is probably a good thing due to filter equality rules.

Nop no longer implements Filter.

Replaced True and False with Self, Desc, Both, All.

Replaced Not with InvertSelf.

Renamed DerefLen to Len.

v0.2.2

Added Fields and TypeFields for micro-optimizing struct shallow walking.

v0.2.1

Walk / GetWalker now support walking into interface{} values, fetching the appropriate cached walker for the given type and filter on the fly.

Added MaybeOr, MaybeAnd, GetTypeFilter, TypeFilterFor for micro-optimizing filter allocations.

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

View Source
const (
	// Don't visit self or descendants. Being zero, this is the default.
	VisNone = 0b_0000_0000

	// Visit self.
	VisSelf = 0b_0000_0001

	// Visit descendants.
	VisDesc = 0b_0000_0010

	// Visit both self and descendants.
	VisBoth = VisSelf | VisDesc

	// Same effect as `rf.VisBoth`. Provided for arithmetic.
	VisAll = 0b_1111_1111
)

Flags constituting the return value of `rf.Filter`. Unknown bits will be ignored.

Variables

This section is empty.

Functions

func DeepFields added in v0.4.0

func DeepFields(typ any) []r.StructField

Shortcut for `rf.TypeDeepFields(rf.DerefType(typ))`.

func Deref added in v0.4.0

func Deref(val any) 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 DerefFunc

func DerefFunc(val any) r.Value

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

func DerefKind

func DerefKind(val any) 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 DerefMap

func DerefMap(val any) r.Value

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

func DerefSlice

func DerefSlice(val any) r.Value

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

func DerefStruct

func DerefStruct(val any) r.Value

Shortcut for `rf.DerefWithKind(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 DerefWithKind added in v0.4.0

func DerefWithKind(src any, 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 any) 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 Fields added in v0.2.2

func Fields(typ any) []r.StructField

Shortcut for `rf.TypeFields(rf.DerefType(typ))`.

func FuncName

func FuncName(val any) string

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

func Interface added in v0.4.0

func Interface(val r.Value) any

Like `reflect.Value.Interface`, but returns nil when value is invalid, and attempts to take an address by calling `rf.ValueAddr`, which is sometimes more efficient. Calling `reflect.Value.Interface` on a non-address makes a heap copy of the underlying value, while indirection can avoid that cost.

func IsColl

func IsColl(val any) 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 any) 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 IsFieldPublic added in v0.5.1

func IsFieldPublic(val r.StructField) bool

True if the given struct field is considered public/exported. Shortcut for `rf.IsPublic(val.PkgPath)`.

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 any) 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 any) 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 ind := 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 any) 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 Len added in v0.3.0

func Len(val any) int

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

func NormNil

func NormNil(val any) any

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 OffsetFields added in v0.4.0

func OffsetFields(typ any) map[uintptr][]r.StructField

Shortcut for `rf.TypeOffsetFields(rf.DerefType(typ))`.

func SliceType

func SliceType(val any) 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 any, Out ~[]Elem, Elem any](src *Src, out *Out)

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 any, Out ~[]Elem, Elem any](src *Src, out *Out, 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 Type added in v0.5.0

func Type[A any]() r.Type

Returns `reflect.Type` of the given type. Differences from `reflect.TypeOf`:

  • Avoids spurious heap escape and copying.

  • Output is always non-nil.

  • When the given type is an interface, including the empty interface `any`, the output is a non-nil `reflect.Type` describing the given interface.

func TypeDeepFields added in v0.4.0

func TypeDeepFields(typ r.Type) []r.StructField

Takes a struct type and returns a slice of its fields, with support for embedded structs. Structs embedded by value (not by pointer) are considered parts of the enclosing struct, rather than fields in their own right, and their fields are included into this function's output. This is NOT equivalent to the fields you would get by iterating over `reflect.Type.NumField`. Not only because it includes the fields of value-embedded structs, but also because it adjusts `reflect.StructField.Index` and `reflect.StructField.Offset` specifically for the given ancestor type. In particular, `reflect.StructField.Offset` of deeply-nested fields is exactly equivalent to the output of `unsafe.Offsetof` for the same parent type and field, which is NOT what you would normally get from the "reflect" package.

For comparison. Normally when using `reflect.Type.FieldByIndex`, the returned fields have both their offset and their index relative to their most immediate parent, rather than the given ancestor. But it's also inconsistent. When using `reflect.Type.FieldByName`, the returned fields have their index relative to the ancestor, but their offset is still relative to their most immediate parent.

This function fixes ALL of that. It gives you fields where offsets AND indexes are all relative to the ancestor.

Like `rf.TypeFields`, this caches and reuses the resulting slice for all future calls for any given type. The resulting slice or its elements must not be mutated.

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 TypeFields added in v0.2.2

func TypeFields(typ r.Type) []r.StructField

Takes a struct type and returns a shallow slice of its fields, similar to what you would get by iterating over `reflect.Type.NumField`. For any given type, caches and reuses the resulting slice for all future calls. The resulting slice or its elements must not be mutated. Note: because this doesn't support embedded structs, for most use cases `rf.TypeDeepFields` is more appropriate.

func TypeKind

func TypeKind(val r.Type) r.Kind

Nil-safe version of `reflect.Type.Kind`. If the input is nil, returns `reflect.Invalid`.

func TypeOffsetFields added in v0.4.0

func TypeOffsetFields(typ r.Type) map[uintptr][]r.StructField

Takes a struct type and returns a mapping of field offsets to fields. Offsets correspond to `unsafe.Offsetof`. The map is built from `rf.TypeDeepFields`, which treats structs embedded by value (not by pointer) as parts of the enclosing struct, including their fields into the output, and adjusts field offsets for compatibility with `unsafe.Offsetof`. Offsets may correspond to multiple fields because fields may be zero-sized. Each included slice has at least one field. Caches and reuses the resulting map for all future calls for any given type. The map or anything contained within must not be mutated.

func ValidFunc

func ValidFunc(val any) r.Value

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

func ValidMap

func ValidMap(val any) r.Value

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

func ValidPtr

func ValidPtr(val any) r.Value

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

func ValidPtrToKind

func ValidPtrToKind(val any, 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 any) r.Value

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

func ValidSliceOf

func ValidSliceOf(val any, 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 any, 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 any) r.Value

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

func ValidTypeFunc

func ValidTypeFunc(val any) r.Type

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

func ValidTypeMap

func ValidTypeMap(val any) r.Type

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

func ValidTypePtr

func ValidTypePtr(val any) r.Type

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

func ValidTypeSlice

func ValidTypeSlice(val any) r.Type

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

func ValidTypeStruct

func ValidTypeStruct(val any) 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 ValueAddr added in v0.4.0

func ValueAddr(val r.Value) r.Value

If the value represents a non-pointer and its address can be taken, returns its address via `reflect.Value.Addr`. Otherwise returns the value as-is. Sometimes handy when taking an address is an optional optimization, such as before calling `reflect.Value.Interface`.

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.

If the input is zero/invalid/nil or the visitor is nil, this is a nop. For slightly better performance, pass a pointer to reduce copying.

See also:

rf.Walker
rf.Filter
rf.Visitor
rf.GetWalker

func WalkFunc

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

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

func WalkPtr added in v0.4.1

func WalkPtr(val any, fil Filter, vis Visitor)

Shortcut for `rf.Walk` on the given value, which must be either a valid pointer or nil. If the value is nil, this is a nop. Requiring a pointer is useful for both efficiency and correctness. Even if the walker doesn't modify anything, passing a pointer reduces copying. If the walker does modify walked values, and you try to walk a non-pointer, you will get uninformative panics from the "reflect" package. This function validates the inputs early, making it easier to catch such bugs.

func WalkPtrFunc added in v0.4.2

func WalkPtrFunc(val any, fil Filter, vis VisitorFunc)

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

func Zero

func Zero(ptr any)

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 All added in v0.3.0

type All struct{}

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

func (All) Visit added in v0.3.0

func (All) Visit(r.Type, r.StructField) byte

Implement `rf.Filter`.

type And

type And [8]Filter

Implementation of `rf.Filter` that combines other filters, AND-ing their outputs via `&`. Nil elements are ignored. If all elements are nil, the output is automatically `VisNone`.

func (And) Visit added in v0.3.0

func (self And) Visit(typ r.Type, field r.StructField) (vis byte)

Implement `rf.Filter`.

type Appender

type Appender[A any] []A

Implements `rf.Visitor` by appending visited non-zero elements.

func (Appender[A]) Filter

func (self Appender[A]) Filter() Filter

Returns a filter that allows to visit only values suitable to be elements of the slice held by the appender.

func (*Appender[A]) Visit

func (self *Appender[A]) 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 Both added in v0.3.0

type Both struct{}

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

func (Both) Visit added in v0.3.0

func (Both) Visit(r.Type, r.StructField) byte

Implement `rf.Filter`.

type Cache

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

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) any

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

type Desc added in v0.3.0

type Desc struct{}

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

func (Desc) Visit added in v0.3.0

func (Desc) Visit(r.Type, r.StructField) byte

Implement `rf.Filter`.

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 ErrStr added in v0.5.2

type ErrStr string

String typedef that implements `error`. Errors of this type can be defined as constants.

func (ErrStr) Error added in v0.5.2

func (self ErrStr) Error() string

Implement `error`.

func (ErrStr) String added in v0.5.2

func (self ErrStr) String() string

Implement `fmt.Stringer`.

type Filter

type Filter interface {
	Visit(r.Type, r.StructField) byte
}

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

The return value of `rf.Filter.Visit` is a combination of two optional flags: `rf.VisSelf` and `rf.VisDesc`. Flags are combined with bitwise OR. The following combinations are known:

return VisNone // Zero value / default.
return VisSelf
return VisDesc
return VisSelf | VisDesc
return VisBoth // Shortcut for the above.

If the flag `rf.VisDesc` is set, we attempt to generate an inner walker that visits the descendants of the current node, such as the elements of a slice, the fields of a struct, the value behind a pointer, or the value referenced by an interface. Otherwise, we don't attempt to generate an inner walker.

If the flag `rf.VisSelf` is set, we generate a walker that invokes `Visitor.Visit` on the current node. Otherwise the resulting walker will not visit the current node, and may possibly be nil.

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{
	TypeFilter[string]{},
	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[string]{},
	&rf.TagFilter{`json`, `fieldName`},
}

See also:

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

func MaybeAnd added in v0.2.1

func MaybeAnd(vals ...Filter) Filter

Micro-optimization for `rf.And`. If the input has NO non-nil filters, returns nil. If the input has ONE non-nil filter, returns that filter, avoiding an allocation of `rf.And{}`. Otherwise combines the filters via `rf.And`.

func MaybeOr added in v0.2.1

func MaybeOr(vals ...Filter) Filter

Micro-optimization for `rf.Or`. If the input has NO non-nil filters, returns nil. If the input has ONE non-nil filter, returns that filter, avoiding an allocation of `rf.Or{}`. Otherwise combines the filters via `rf.Or`.

type IfaceFilter added in v0.3.0

type IfaceFilter[_ any] struct{}

Implementation of `rf.Filter` that allows to visit values whose types implement the given interface BY POINTER. If the type is nil, this won't visit anything. The type must represent an interface, otherwise this will panic. The visitor must explicitly take value address:

func visit(val r.Value, _ r.StructField) {
	val.Addr().Interface().(SomeInterface).SomeMethod()
}

func (IfaceFilter[A]) Visit added in v0.3.0

func (IfaceFilter[A]) Visit(typ r.Type, _ r.StructField) byte

Implement `rf.Filter`.

type InvertSelf added in v0.3.0

type InvertSelf [1]Filter

Implementation of `rf.Filter` that inverts the "self" bit of the inner filter, without changing the other flags. If the inner filter is nil, this always returns `rf.VisNone`.

func (InvertSelf) Visit added in v0.3.0

func (self InvertSelf) Visit(typ r.Type, field r.StructField) byte

Implement `rf.Filter`.

type KindFilter added in v0.5.0

type KindFilter r.Kind

Implementation of `rf.Filter` that allows to visit values of the given `reflect.Kind`. If the kind is `reflect.Invalid`, this won't visit anything.

Untested.

func (KindFilter) Visit added in v0.5.0

func (self KindFilter) Visit(typ r.Type, _ r.StructField) byte

Implement `rf.Filter`.

type Nop

type Nop struct{}

No-op implementation of both `rf.Visitor` that does nothing upon visit.

func (Nop) Visit

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

Implement `rf.Visitor`.

type Or

type Or [8]Filter

Implementation of `rf.Filter` that combines other filters, OR-ing their outputs via `|`. Nil elements are ignored. If all elements are nil, the output is automatically `VisNone`.

func (Or) Visit added in v0.3.0

func (self Or) Visit(typ r.Type, field r.StructField) (vis byte)

Implement `rf.Filter`.

type Path added in v0.4.0

type Path []int

Alias of `[]int` (which is used for struct field paths such as `reflect.StructField.Index`) with some shortcuts relevant for efficient and correct walking of struct types.

func (*Path) Add added in v0.4.0

func (self *Path) Add(vals []int) Rev

Appends elements to the path, returning a stack-allocatable value that can revert the path to the previous length by reslicing, keeping any capacity that was added by the append. Usage:

defer path.Add(field.Index).Reset()

func (Path) Copy added in v0.4.0

func (self Path) Copy() Path

Returns a copy whose length and capacity are equal to the length of the original. 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`).

type Rev added in v0.4.0

type Rev struct {
	Path *Path
	Len  int
}

Returned by `(*rf.Path).Add`. Syntactic shortcut for appending to a path for the duration of a function, reverting it back at the end. Should be transient, stack-allocated, and basically free to use.

func (Rev) Reset added in v0.4.0

func (self Rev) Reset()

Reslices `self.Path`, resetting its length to `self.Len` while keeping the capacity. Usage:

defer path.Add(field.Index).Reset()

type Self added in v0.3.0

type Self struct{}

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

func (Self) Visit added in v0.3.0

func (Self) Visit(r.Type, r.StructField) byte

Implement `rf.Filter`.

type ShallowIfaceFilter added in v0.3.2

type ShallowIfaceFilter[_ any] struct{}

Like `rf.IfaceFilter`, but visits either self or descendants, not both. In other words, once it finds a node that implements the given interface (by pointer), it allows to visit that node and stops there, without walking its descendants.

func (ShallowIfaceFilter[A]) Visit added in v0.3.2

func (ShallowIfaceFilter[A]) Visit(typ r.Type, _ r.StructField) byte

Implement `rf.Filter`.

type TagFilter

type TagFilter [2]string

Implementation of `rf.Filter` that allows to visit values whose struct tag has a specific tag with a specific value, such as tag "json" with value "-". It also allows to visit descendants.

Known limitation: can't differentiate empty tag from missing tag.

func (TagFilter) Visit added in v0.3.0

func (self TagFilter) Visit(_ r.Type, field r.StructField) byte

Implement `rf.Filter`.

type TypeFilter

type TypeFilter[_ any] struct{}

Implementation of `rf.Filter` that allows to visit values of this specific type. If the type is nil, this won't visit anything. The type may be either concrete or an interface. It also allows to visit descendants.

func (TypeFilter[A]) Visit added in v0.3.0

func (TypeFilter[A]) Visit(typ r.Type, _ r.StructField) byte

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.

A walker takes a visitor and walks that visitor through a structure, invoking the visitor on the nodes approved by the filter. See the interface `rf.Visitor`.

For simplicity and efficiency, walkers generated by this package don't additionally assert that the `reflect.Value` provided at the top level has the same type for which the walker was generated. When using `rf.Walk` or `rf.WalkFunc`, it always matches. Otherwise, it's your responsibility to pass the matching input first to `rf.GetWalker`, then to `.Walk`. For simplicity, walkers also assume that the visitor is non-nil.

This package currently does NOT support walking into maps, for two reasons: unclear semantics and inefficiency. It's unclear if we should walk keys, values, or key-value pairs, and how that affects the rest of the walking API. Currently in Go 1.17, reflect-based map walking has horrible inefficiencies which can't be amortized by 3rd party code. It would be a massive performance footgun.

This package does support walking into interface values included into other structures, but at an efficiency loss. In general, our walking mechanism relies on statically determining what we should and shouldn't visit, which is possible only with static types. Using interfaces as dynamically-typed containers of unknown values defeats this design by forcing us to always visit each of them, and may produce significant slowdowns. However, while visiting each interface value is an unfortunate inefficiency, walking the value REFERENCED by an interface is as precise and efficient as with static types.

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