refut

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: May 7, 2021 License: Unlicense Imports: 3 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://godoc.org/github.com/mitranim/refut.

Changelog

v0.1.3

Added IsZero, IsRvalZero.

v0.1.2

Changed to Unlicense.

v0.1.1

  • Bugfix: TraverseStruct and TraverseStructRval no longer attempt to traverse nil embedded struct pointers.
  • TraverseStruct and TraverseStructRval now allow a nil struct pointer as input, without traversing its fields. This behavior is consistent with nil embedded struct pointers.
  • Added RvalDerefAlloc.
  • Added RvalFieldByPathAlloc.

v0.1.0

First tagged release.

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

Overview

Utilities missing from the "reflect" package: easier struct traversal, deep dereferencing, various boolean tests such as generic `IsNil`, and some more. Small and dependency-free.

Naming Conventions

• Functions that take a `reflect.Value` have "Rval" in the name.

• Functions that take a `reflect.Type` have "Rtype" in the name.

API Considerations

In general, utils for struct traversal can be implemented as function that take closures, or as stateful iterator objects. Both approaches were tested and demonstrated similar performance characteristics. The iterator-based approach can be more convenient to use because it doesn't interrupt the control flow of the caller code. However, the closure-based approach is much simpler to implement and get right. It's also much simpler to wrap, making it easier for users of this package to implement their own iterator functions in terms of the existing ones.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingFunc  = Error(`refut: missing callback function`)
	ErrInvalidType  = Error(`refut: received invalid type`)
	ErrInvalidValue = Error(`refut: received invalid value`)
)

Functions

func IsColl

func IsColl(val interface{}) bool

Returns true if the kind of the provided value is a collection. See `IsRkindColl` 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 IsEmptyColl

func IsEmptyColl(val interface{}) bool

Returns true if the input belongs to one of the kinds "reflect" considers collections (see `IsRkindColl`), and has the length of zero. Nils are included.

func IsNil

func IsNil(val interface{}) bool

Similar to `X == nil`, but returns true if the input is a non-nil `interface{}` whose underlying concrete type is nilable (pointer, slice, map, channel, func) and its underlying concrete val is nil.

func IsNilable

func IsNilable(val interface{}) bool

Returns true if the kind of the provided value is nilable. See `IsRkindNilable` for further details. As a special case, this returns true for a nil input, even though it carries no type information.

func IsRkindColl

func IsRkindColl(kind reflect.Kind) bool

Returns 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 IsRkindNilable

func IsRkindNilable(kind reflect.Kind) bool

Returns true for the kinds "reflect" considers nilable: chan, func, interface, map, pointer, or slice. These are the kinds for which it's safe to call `reflect.Value.IsNil` without panicking.

func IsRvalEmptyColl

func IsRvalEmptyColl(rval reflect.Value) bool

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

func IsRvalNil

func IsRvalNil(rval reflect.Value) bool

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

func IsRvalZero added in v0.1.3

func IsRvalZero(rval reflect.Value) bool

Variant of `reflect.Value.IsZero` that returns false for invalid values instead of panicking.

func IsSfieldExported

func IsSfieldExported(sfield reflect.StructField) bool

func IsZero added in v0.1.3

func IsZero(val interface{}) bool

Returns true if the input is a zero value of its type. As a special case, returns true if the input itself is nil (carries no type information).

func KindOf

func KindOf(val interface{}) reflect.Kind

Shortcut for `reflect.TypeOf(val).Kind()`. Returns `reflect.Invalid` for a nil input.

func RtypeDeref

func RtypeDeref(rtype reflect.Type) reflect.Type

Takes a `reflect.Type` and dereferences as many times as needed until it's no longer a pointer type. A nil type is ok and returns nil.

Note: if the type is defined recursively as a pointer to itself, this will loop forever.

func RvalDeref

func RvalDeref(rval reflect.Value) reflect.Value

Takes a `reflect.Value` and dereferences as many times as needed until it's no longer a pointer. Panics if any pointer in the sequence is nil.

func RvalDerefAlloc

func RvalDerefAlloc(rval reflect.Value) reflect.Value

Takes a `reflect.Value` and dereferences as many times as needed until it's no longer a pointer, while allocating intermediary values as necessary. If the input value is a pointer, it must be settable or non-nil, otherwise this causes a panic.

Example:

var val ***string
rval := refut.RvalDerefAlloc(reflect.ValueOf(&val))
rval.SetString("hello world")
fmt.Println(***val) // "hello world"

func RvalFieldByPathAlloc

func RvalFieldByPathAlloc(rval reflect.Value, path []int) reflect.Value

Variant of `reflect.Value.FieldByIndex` that allocates intermediary values for fields which are struct pointers. For example:

type Inner struct { Value string }
type Outer struct { *Inner }

var outer Outer
rval := refut.RvalFieldByPathAlloc(reflect.ValueOf(&outer), []int{0, 0})
rval.SetString("hello world")
fmt.Println(outer.Value) // "hello world"

Unlike `reflect.Value.FieldByIndex`, this function also allows the input value to be a non-nil struct pointer of any depth, as seen in the example above.

func TagIdent

func TagIdent(tag string) string

Takes a struct field tag and returns the identifier part contained in it, as long as the tag follows the convention established by `json:""` tags:

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

Ident "-" is converted to "".

Usage:

ident := TagIdent(sfield.Tag.Get("json"))
ident := TagIdent(sfield.Tag.Get("db"))

func TraverseStruct

func TraverseStruct(val interface{}, fun func(reflect.Value, reflect.StructField, []int) error) error

Takes a struct value and traverses its fields, calling the provided function for each field. Skips non-exported (private) fields. Traverses the fields of embedded structs as if they were part of the enclosing struct.

The callback receives arguments for a specific struct field:

• `reflect.Value`: field value.

• `reflect.StructField`: field description.

• `[]int`: path to the field, suitable for `reflect.Value.FieldByIndex` or `RvalFieldByPathAlloc` on the root value. See `TraverseStructType` for notes about the path.

WARNING: the path slice is allocated once and mutated between iterations. To store the path for later use, you MUST copy the slice.

The input may be a struct pointer, which is automatically dereferenced. Nil struct pointers at the root level or as embedded fields are okay and won't be traversed.

If the input is not a struct, or if the callback is nil, this function panics. Returned errors are always from the callback.

func TraverseStructRtype

func TraverseStructRtype(rtype reflect.Type, fun func(reflect.StructField, []int) error) error

Variant of `TraverseStructType` that takes a `reflect.Type` as the input. See `TraverseStructType` for the documentation.

If the input is not a struct type, or if the callback is nil, this function panics. Returned errors are always from the callback.

func TraverseStructRval

func TraverseStructRval(rval reflect.Value, fun func(reflect.Value, reflect.StructField, []int) error) error

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

If the input is not a struct, or if the callback is nil, this function panics. Returned errors are always from the callback.

func TraverseStructType

func TraverseStructType(val interface{}, fun func(reflect.StructField, []int) error) error

Takes a struct value and traverses the fields of its type, calling the provided function for each field. Skips non-exported (private) fields. Traverses the fields of embedded structs as if they were part of the enclosing struct.

This function is provided for convenience; it ignores the actual value of its input. The input may be a struct or a struct pointer. Nil pointers are okay.

The callback receives arguments for a specific struct field:

• `reflect.StructField`: field description.

• `[]int`: path to the field.

The reason the path is a slice of ints, rather than one int, is because addressing a field of an embedded struct requires more than one index. The path is suitable for `reflect.Type.FieldByIndex` on the root type or `RvalFieldByPathAlloc` on a value of that type.

WARNING: the path slice is allocated once and mutated between iterations. To store the path for later use, you MUST copy the slice.

If the input is not a struct type, or if the callback is nil, this function panics. Returned errors are always from the callback.

Types

type Error

type Error string

Type of errors generated by this package.

func (Error) Error

func (self Error) Error() string

Implement `error`.

func (Error) Is

func (self Error) Is(other error) bool

Implement a hidden interface in "errors".

Jump to

Keyboard shortcuts

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