README
¶
Package set
is a performant reflect wrapper supporting loose type conversion, struct mapping and population, and slice building.
The godoc
documentation has detailed information and many examples but following is the high-level view.
Type Coercion
Type coercion allows assignment from loosey-goosey sources -- for example incoming string data -- to strongly typed Go types.
var t T // T is a target data type, t is a variable of that type.
var s S // S is a source data type, s is a variable of that type.
set.V(&t).To(s) // Sets s into t with a "best effort" approach.
Struct Population with Getter
set.Value
has two methods, Fill
and FillByTag
, that use the set.Getter
interface as the provider for data to populate a struct and its hierarchy.
For convenience:
set.GetterFunc
allows plain functions to be used as aset.Getter
similar tohttp.HandlerFunc
.set.MapGetter
allows eithermap[string]T
ormap[interface{}]T
to be used as aset.Getter
.
Struct Mapping
set.Mapper
is a powerful and highly configurable struct mapping facility.
A mapper will traverse a struct hiearchy and create a 1:1
mapping of friendly names
to traversal information
. The friendly names can then be used to target associated fields within a struct value and its hierarchy.
Example usages of a mapper are to map CSV column headings to struct fields, database column names to struct fields, or creating a generic struct copier to marshal structs across different domains or boundaries in your application architecture.
set.Mapper
contains several configuration fields that can be used to fully customize the generated friendly names
:
- Choose how nested names are combined:
VendorName
,Vendor_Name
,Vendor.Name
,vendor_name
, etc. - Specify multiple tags in order of preference:
db
tag values can have higher precedence thanjson
tags - Elevate types into a higher namespace: see the Mapper example(s)
- Specify types that are ignored and don't get mapped.
- Specify types that are treated as scalars: useful for sql.Null* types or similar
BoundMapping and PreparedMapping
Once a set.Mapper
(described above) is created it can return BoundMapping
or PreparedMapping
types that are bound to Go structs. In turn BoundMapping
and PreparedMapping
provide performant access to the bound data via the friendly names generated by the mapper.
A BoundMapping
provides an adhoc access to struct fields; each method takes the mapped name of the field to access. An example use case for BoundMapping
is populating data when some of the data may be missing and you may not set data for every possible mapped field.
A PreparedMapping
is similar to a prepared SQL statement and the access plan must be set with a call to its Plan
method. An example use case for PreparedMapping
is populating CSV data or database rows where every row is guaranteed to access the same fields in the same order.
Performance Notes
Package reflect
is always slower than code not using reflect
. A considerable effort has been spent designing and implementing this package to reduce reflect overhead.
reflect
data is generally only gathered once (via reflect.TypeOf, reflect.ValueOf) when first encountering a type. This data is cached and retrieved from cache on further encounters with repeated types.- Value assigning is generally attempted first with type switches and then falls back to
reflect
. This strategy is heavily used during type coercion. - Appropriate types in this package have a
Rebind
method.Rebind
will swap a "bound" Go type with a new incoming instance without making additional expensive calls intoreflect
. The outgoing and incoming types must be compatible but this is the expected usage in tight loops building slices of data.
Additionally this package attempts to be low allocation so as not to overwhelm the garbage collector.
- Some of the methods on BoundMapping and PreparedMapping allow a dest slice to be pre-allocated.
- BoundMapping, PreparedMapping, and Value are created and returned as structs instead of pointers.
API Consistency and Breaking Changes
I am making a very concerted effort to break the API as little as possible while adding features or fixing bugs. However this software is currently in a pre-1.0.0 version and breaking changes are allowed under standard semver. As the API approaches a stable 1.0.0 release I will list any such breaking changes here and they will always be signaled by a bump in minor version.
-
0.4.0 ⭢ 0.5.0
-
README-0.4.0-to-0.5.0.md outlines many of the package changes, reasoning, and benchmarks
-
Remove erroneous documentation for
Value.To
method.
The documentation indicated that when Dst and Src are both pointers with same level of indirection that direct assignment was performed. This is not true. The Value type uses the values at the end of pointers and pointer chains and therefore does not perform direct assignment of pointer values.
-
-
0.3.0 ⭢ 0.4.0
set.Mapper has new field TaggedFieldsOnly.TaggedFieldsOnly=false
means no change in behavior.TaggedFieldsOnly=true
means set.Mapper only maps exported fields with struct tags. -
0.2.3 ⭢ 0.3.0
set.BoundMapping.Assignables has a second argument allowing you to pre-allocate the slice that is returned; you can also set it tonil
to keep current behavior.
Documentation
¶
Overview ¶
Package set is a performant reflect wrapper supporting loose type conversion, struct mapping and population, and slice building.
Type Coercion ¶
Value and its methods provide a generous facility for type coercion:
var t T // T is a target data type, t is a variable of that type. var s S // S is a source data type, s is a variable of that type. set.V(&t).To(s) // Sets s into t with a "best effort" approach.
See documentation and examples for Value.To.
The examples subdirectory contains additional examples for the Value type.
Finally you may wish to work directly with the coerce subpackage, which is the workhorse underneath Value.To.
Populating Structs by Lookup Function ¶
See examples for GetterFunc.
Populating Structs by Map ¶
See example for MapGetter.
Struct Mapping ¶
Struct and struct hierarchies can be mapped to a flat list of string keys. This is useful for deserializers and unmarshalers that need to convert a friendly string such as a column name or environment variable and use it to locate a target field within a struct or its hierarchy.
See examples for Mapper.
Mapping, BoundMapping, and PreparedMapping ¶
Once an instance of Mapper is created it can be used to create Mapping, BoundMapping, and PreparedMapping instances that facilitate struct traversal and population.
BoundMapping and PreparedMapping are specialized types that bind to an instance of T and allow performant access to T's fields or values.
See examples for Mapper.Bind and Mapper.Prepare.
In tight-loop scenarios an instance of BoundMapping or PreparedMapping can be bound to a new instance T with the Rebind method.
See examples for BoundMapping.Rebind and PreparedMapping.Rebind.
If neither BoundMapping nor PreparedMapping are suitable for your use case you can call Mapper.Map to get a general collection of data in a Mapping. The data in a Mapping may be helpful for creating your own traversal or population algorithm without having to dive into all the complexities of the reflect package.
BoundMapping vs PreparedMapping ¶
BoundMapping allows adhoc access to the bound struct T. You can set or retrieve fields in any order. Conceptually a BoundMapping is similar to casting a struct and its hierarchy into a map of string keys that target fields within the hierarchy.
PreparedMapping requires an access plan to be set by calling the Plan method. Once set the bound value's fields must be set or accessed in the order described by the plan. A PreparedMapping is similar to a prepared SQL statement.
Of the two PreparedMapping yields better performance. You should use PreparedMapping when you know every bound value will have its fields accessed in a determinate order. If fields will not be accessed in a determinate order then you should use a BoundMapping.
BoundMapping methods require the field(s) as arguments; in some ways this can help with readability as your code will read:
b.Set("FooField", "Hello") b.Set("Number", 100)
whereas code using PreparedMapping will read:
p.Set("Hello") p.Set(100)
What is Rebind ¶
The BoundMapping, PreparedMapping, and Value types internally contain meta data about the types they are working with. Most of this meta data is obtained with calls to reflect and calls to reflect can be expensive.
In one-off scenarios the overhead of gathering meta data is generally not a concern. But in tight-loop situations this overhead begins to add up and is a key reason reflect has gained a reputation for being slow in the Go community.
Where appropriate types in this package have a Rebind method. Rebind swaps the current value being worked on with a new incoming value without regathering reflect meta data. When used appropriately with Rebind the BoundMapping, PreparedMapping, and Value types become much more performant.
A Note About Package Examples ¶
Several examples ignore errors for brevity:
_ = p.Plan(...) // error ignored for brevity _ = b.Set(...) // error ignored for brevity
This is a conscious decision because error checking is not the point of the examples. However in production code you should check errors appropriately.
Example (BoundMappingErrors) ¶
Output: set: BoundMapping.Assignables: read only value: hint=[call to Mapper.Bind(set_test.Company) should have been Mapper.Bind(*set_test.Company)] set: BoundMapping.Field: read only value: hint=[call to Mapper.Bind(set_test.Company) should have been Mapper.Bind(*set_test.Company)] set: BoundMapping.Fields: read only value: hint=[call to Mapper.Bind(set_test.Company) should have been Mapper.Bind(*set_test.Company)] set: BoundMapping.Set: read only value: hint=[call to Mapper.Bind(set_test.Company) should have been Mapper.Bind(*set_test.Company)] set: BoundMapping.Assignables: unknown field: field [foobar] not found in type *set_test.Company set: BoundMapping.Field: unknown field: field [foobar] not found in type *set_test.Company set: BoundMapping.Fields: unknown field: field [foobar] not found in type *set_test.Company set: BoundMapping.Set: unknown field: field [foobar] not found in type *set_test.Company
Example (CSVUnmarshaler) ¶
Output: ID=1 06 Hoepker Court, Jacksonville, Florida 32209 ID=2 92 Cody Hill, Falls Church, Virginia 22047 ID=3 242 Burning Wood Terrace, Fort Worth, Texas 76105 ID=4 41 Clarendon Pass, Fort Myers, Florida 33913 ID=1 2607 Hanson Junction, Tuscaloosa, Alabama 35487 ID=2 2 Sherman Place, Bakersfield, California 93305 ID=3 4 Porter Place, Kansas City, Missouri 64199 ID=4 23 Sachtjen Alley, New York City, New York 10160
Example (MapperErrors) ¶
Output: set: Mapper.Bind: read only value: hint=[call to Mapper.Bind(set_test.Company) should have been Mapper.Bind(*set_test.Company)] set: Mapper.Prepare: read only value: hint=[call to Mapper.Prepare(set_test.Company) should have been Mapper.Prepare(*set_test.Company)]
Example (PreparedMappingErrors) ¶
Output: set: PreparedMapping.Assignables: read only value: hint=[call to Mapper.Prepare(set_test.Company) should have been Mapper.Prepare(*set_test.Company)] set: PreparedMapping.Field: read only value: hint=[call to Mapper.Prepare(set_test.Company) should have been Mapper.Prepare(*set_test.Company)] set: PreparedMapping.Fields: read only value: hint=[call to Mapper.Prepare(set_test.Company) should have been Mapper.Prepare(*set_test.Company)] set: PreparedMapping.Plan: read only value: hint=[call to Mapper.Prepare(set_test.Company) should have been Mapper.Prepare(*set_test.Company)] set: PreparedMapping.Field: no plan: hint=[call PreparedMapping.Plan to prepare access plan for *set_test.Company] set: PreparedMapping.Set: no plan: hint=[call PreparedMapping.Plan to prepare access plan for *set_test.Company] set: PreparedMapping.Plan: unknown field: field [foobar] not found in type *set_test.Company set: PreparedMapping.Field: attempted access extends plan: value of *set_test.Company set: PreparedMapping.Set: attempted access extends plan: value of *set_test.Company
Example (ValueErrors) ¶
Output: set: Value.Append: unsupported: nil value: hint=[set.V(nil) was called] set: Value.Append: read only value: []int is not writable: hint=[call to set.V([]int) should have been set.V(*[]int)] set: Value.Append: unsupported: can not append to int set: Value.FieldByIndex: unsupported: nil value: hint=[set.V(nil) was called] set: Value.FieldByIndex: read only value: set_test.S is not writable: hint=[call to set.V(set_test.S) should have been set.V(*set_test.S)] set: Value.FieldByIndex: unsupported: empty index set: Value.FieldByIndex: index out of bounds: index 2 exceeds max 1 set: Value.FieldByIndex: unsupported: want struct but got int set: Value.Zero: unsupported: nil value: hint=[set.V(nil) was called] set: Value.Zero: read only value: int is not writable: hint=[call to set.V(int) should have been set.V(*int)] set: Value.To: unsupported: nil value: hint=[set.V(nil) was called] set: Value.To: read only value: int is not writable: hint=[call to set.V(int) should have been set.V(*int)]
Index ¶
- Variables
- func Writable(v reflect.Value) (V reflect.Value, CanWrite bool)
- type BoundMapping
- func (b BoundMapping) Assignables(fields []string, rv []interface{}) ([]interface{}, error)
- func (b BoundMapping) Copy() BoundMapping
- func (b BoundMapping) Err() error
- func (b BoundMapping) Field(field string) (Value, error)
- func (b BoundMapping) Fields(fields []string, rv []interface{}) ([]interface{}, error)
- func (b *BoundMapping) Rebind(v interface{})
- func (b *BoundMapping) Set(field string, value interface{}) error
- type CanPanic
- type Field
- type Getter
- type GetterFunc
- type Mapper
- type Mapping
- type PreparedMapping
- func (p PreparedMapping) Assignables(rv []interface{}) ([]interface{}, error)
- func (p PreparedMapping) Copy() PreparedMapping
- func (p PreparedMapping) Err() error
- func (p *PreparedMapping) Field() (Value, error)
- func (p PreparedMapping) Fields(rv []interface{}) ([]interface{}, error)
- func (p *PreparedMapping) Plan(fields ...string) error
- func (p *PreparedMapping) Rebind(v interface{})
- func (p *PreparedMapping) Set(value interface{}) error
- type SliceValue
- type TypeInfo
- type TypeInfoCache
- type TypeList
- type Value
- func (v Value) Append(items ...interface{}) error
- func (v Value) Copy() Value
- func (v Value) FieldByIndex(index []int) (reflect.Value, error)
- func (v Value) FieldByIndexAsValue(index []int) (Value, error)
- func (v Value) Fields() []Field
- func (v Value) FieldsByTag(key string) []Field
- func (v Value) Fill(getter Getter) error
- func (v Value) FillByTag(key string, getter Getter) error
- func (v Value) NewElem() (Value, error)
- func (v *Value) Rebind(arg interface{})
- func (v Value) To(arg interface{}) error
- func (v Value) Zero() error
Examples ¶
- Package (BoundMappingErrors)
- Package (CSVUnmarshaler)
- Package (MapperErrors)
- Package (PreparedMappingErrors)
- Package (ValueErrors)
- BoundMapping.Rebind
- BoundMapping.Rebind (Panic)
- BoundMapping.Rebind (ReflectValue)
- GetterFunc
- GetterFunc (FillByTag)
- MapGetter
- Mapper
- Mapper (TreatAsScalar)
- Mapper (TreatAsScalarTime)
- Mapper.Bind
- Mapper.Bind (ElevatedEmbed)
- Mapper.Bind (Embedded)
- Mapper.Bind (Nesting)
- Mapper.Bind (ReflectValue)
- Mapper.Prepare
- Mapper.Prepare (ElevatedEmbed)
- Mapper.Prepare (Embedded)
- Mapper.Prepare (Nesting)
- Mapper.Prepare (ReflectValue)
- PreparedMapping.Rebind
- PreparedMapping.Rebind (Panic)
- PreparedMapping.Rebind (ReflectValue)
- Slice
- Slice (Errors)
- Value.Rebind
- Value.Rebind (Panic)
- Value.Rebind (ReflectValue)
- Value.Rebind (Value)
- Value.To
- Value.To (ScalarToSlice)
- Value.To (SliceToScalar)
- Value.To (SliceToSlice)
- Writable
Constants ¶
This section is empty.
Variables ¶
var ( // ErrIndexOutOfBounds is returned when an index operation exceeds a bounds check. ErrIndexOutOfBounds = errors.New("index out of bounds") // ErrInvalidSlice is returned by NewSlice when the in coming value is not pointer-to-slice. ErrInvalidSlice = errors.New("invalid slice") // ErrNoPlan is returned when a PreparedMapping does not have a valid access plan. ErrNoPlan = errors.New("no plan") // ErrPlanOutOfBounds is returned when an access to a PreparedMapping exceeds the // fields specified by the earlier call to Plan. ErrPlanOutOfBounds = errors.New("attempted access extends plan") // ErrReadOnly is returned when an incoming argument is expected to be passed by address // but is passed by value instead. ErrReadOnly = errors.New("read only value") // ErrUnknownField is returned by BoundMapping and PreparedMapping when given field // has no correlating mapping within the struct hierarchy. ErrUnknownField = errors.New("unknown field") // ErrUnsupported is returned when an assignment or coercion is incompatible due to the // destination and source type(s). ErrUnsupported = errors.New("unsupported") )
var DefaultMapper = &Mapper{
Join: "_",
}
DefaultMapper joins names by "_" but performs no other modifications.
var Panics = CanPanic{}
Panics is a global instance of CanPanic; it is provided for convenience.
var TypeCache = NewTypeInfoCache()
TypeCache is a global TypeInfoCache
Functions ¶
Types ¶
type BoundMapping ¶
type BoundMapping struct {
// contains filtered or unexported fields
}
BoundMapping is returned from Mapper's Bind method.
A BoundMapping must not be copied except via its Copy method.
A BoundMapping should be used in iterative code that needs to read or mutate many instances of the same struct. Bound mappings allow for adhoc or indeterminate field access within the bound data.
// adhoc access means different fields can be accessed between calls to Rebind() var a, b T bound := myMapper.Map(&a) bound.Set("Field", 10) bound.Set("Other", "Hello") bound.Rebind(&b) bound.Set("Bar", 27)
In the preceding example the BoundMapping is first bound to a and later bound to b and each instance had different field(s) accessed.
func (BoundMapping) Assignables ¶
func (b BoundMapping) Assignables(fields []string, rv []interface{}) ([]interface{}, error)
Assignables returns a slice of pointers to the fields in the currently bound struct in the order specified by the fields argument.
To alleviate pressure on the garbage collector the return slice can be pre-allocated and passed as the second argument to Assignables. If non-nil it is assumed len(fields) == len(rv) and failure to provide an appropriately sized non-nil slice will cause a panic.
During traversal this method will allocate struct fields that are nil pointers.
An example use-case would be obtaining a slice of pointers for Rows.Scan() during database query results.
func (BoundMapping) Copy ¶ added in v0.3.0
func (b BoundMapping) Copy() BoundMapping
Copy creates an exact copy of the BoundMapping.
One use case for Copy is to create a set of BoundMappings early in a program's init phase. During later execution when a BoundMapping is needed for type T it can be obtained by calling Copy on the cached BoundMapping for that type.
func (BoundMapping) Err ¶
func (b BoundMapping) Err() error
Err returns an error that may have occurred during repeated calls to Set(); it is reset on calls to Rebind()
func (BoundMapping) Field ¶
func (b BoundMapping) Field(field string) (Value, error)
Field returns the Value for field.
func (BoundMapping) Fields ¶ added in v0.3.0
func (b BoundMapping) Fields(fields []string, rv []interface{}) ([]interface{}, error)
Fields returns a slice of values to the fields in the currently bound struct in the order specified by the fields argument.
To alleviate pressure on the garbage collector the return slice can be pre-allocated and passed as the second argument to Fields. If non-nil it is assumed len(fields) == len(rv) and failure to provide an appropriately sized non-nil slice will cause a panic.
During traversal this method will allocate struct fields that are nil pointers.
An example use-case would be obtaining a slice of query arguments by column name during database queries.
func (*BoundMapping) Rebind ¶
func (b *BoundMapping) Rebind(v interface{})
Rebind will replace the currently bound value with the new variable v.
v must have the same type as the original value used to create the BoundMapping otherwise a panic will occur.
As a convenience Rebind allows v to be an instance of reflect.Value. This prevents unnecessary calls to reflect.Value.Interface().
Example (Panic) ¶
Output: mismatching types during Rebind; have *set_test.S and got *set_test.Different
func (*BoundMapping) Set ¶
func (b *BoundMapping) Set(field string, value interface{}) error
Set effectively sets V[field] = value.
type CanPanic ¶
type CanPanic struct{}
CanPanic is a namespace for operations prioritizing speed over type safety or error checking. Reach for this namespace when your usage of the `set` package is carefully crafted to ensure panics will not result from your actions.
Methods within CanPanic will not validate that points are non-nil.
It is strongly encouraged to create suitable `go tests` within your project when reaching for CanPanic.
You do not need to create or instantiate this type; instead you can use the global `var Panics`.
type Field ¶
type Field struct { Value Value Field reflect.StructField TagValue string }
Field is a struct field; it contains a Value and a reflect.StructField.
type Getter ¶
type Getter interface { // Get accepts a name and returns the value. Get(name string) interface{} }
Getter returns a value by name.
type GetterFunc ¶
type GetterFunc func(name string) interface{}
GetterFunc casts a function into a Getter.
func (GetterFunc) Get ¶
func (me GetterFunc) Get(name string) interface{}
Get accepts a name and returns the value.
type Mapper ¶
type Mapper struct { // If the types you wish to map contain embedded structs or interfaces you do not // want to map to string names include those types in the Ignored member. // // See also NewTypeList(). Ignored TypeList // Struct fields that are also structs or embedded structs will have their name // as part of the generated name unless it is included in the Elevated member. // // See also NewTypeList(). Elevated TypeList // Types in this list are treated as scalars when generating mappings; in other words // their exported fields are not mapped and the mapping created targets the type as // a whole. This is useful when you want to create mappings for types such as sql.NullString // without traversing within the sql.NullString itself. TreatAsScalar TypeList // A list of struct tags that will be used for name generation in order of preference. // An example would be using this feature for both JSON and DB field name specification. // If most of your db and json names match but you occasionally want to override the json // struct tag value with the db struct tag value you could set this member to: // []string{ "db", "json" } // struct tag `db` used before struct tag `json` Tags []string // When TaggedFieldsOnly is true the Map() method only maps struct fields that have tags // matching a value in the Tags field. In other words exported tag-less struct fields are not // mapped. TaggedFieldsOnly bool // Join specifies the string used to join generated names as nesting increases. Join string // If set this function is called when the struct field name is being used as // the generated name. This function can perform string alteration to force all // names to lowercase, string replace, etc. Transform func(string) string // contains filtered or unexported fields }
Mapper creates Mapping instances from structs and struct hierarchies.
Mapper allows you to take any struct or struct hierarchy and flatten it to a set of key or column names that index into the hierarchy.
Each of the public fields on Mapper controls an aspect of its behavior in order to provide fine grained control over how key names are generated or how types unknown to the set package are treated.
Instantiate mappers as pointers:
myMapper := &set.Mapper{}
Example ¶
Output: [0 0] Pk [0 1] CreatedTime [0 2] UpdatedTime [1] Name [2] Age lowercase with dot separators [0 0] commondb.pk [0 1] commondb.createdtime [0 2] commondb.updatedtime [1] name [2] age specify tags [0 0] common_pk [0 1] common_created_time [0 2] common_updated_time [1] name [2] age
Example (TreatAsScalar) ¶
Output: Without TreatAsScalar [0 0] N.String [0 1] N.Valid TreatAsScalar [0] N
func (*Mapper) Bind ¶
func (me *Mapper) Bind(I interface{}) (BoundMapping, error)
Bind creates a BoundMapping that is initially bound to I.
BoundMappings are provided for performance critical code that needs to read or mutate many instances of the same type repeatedly without constraint on field access between instances.
See documentation for BoundMapping for more details.
I must be an addressable type.
func (*Mapper) Map ¶
Map adds T to the Mapper's list of known and recognized types.
Map is goroutine safe. Multiple goroutines can call Map() simultaneously and the returned Mappings will behave identically. However if multiple goroutines simultaneously call Map(T) for the same type each goroutine may receiver a Mapping with its own underlying memory. If you require returned Mappings to use shared memory for the slice and map members then you should call Map(T) from a high level goroutine to build up the cache before calling it from other goroutines.
Mappings that are returned are shared resources and should not be altered in any way. If this is your use-case then create a copy of the Mapping with Mapping.Copy.
func (*Mapper) Prepare ¶ added in v0.5.0
func (me *Mapper) Prepare(I interface{}) (PreparedMapping, error)
Prepare creates a PreparedMapping that is initially bound to I.
PreparedMappings are provided for performance critical code that needs to read or mutate many instances of the same type repeatedly and for every instance the same fields will be accessed in the same order.
See documentation for PreparedMapping for more details.
I must be an addressable type.
type Mapping ¶
type Mapping struct { // Keys contains the names generated by the Mapper that created this Mapping. // // See Mapper documentation for information on controlling how and what names // are generated. Keys []string // Indeces contains each mapped field's index as an int slice ([]int) such as // would be appropriate for passing to reflect.Value.FieldByIndex([]int). // // However bear in mind reflect.Value.FieldByIndex([]int) essentially requires that // none of the intermediate fields described by the index are pointers or nil. // // The Value type in this package also has a FieldByIndex([]int) method. Value.FieldByIndex([]int) // will traverse and instantiate pointers and pointer chains where as // reflect.Value.FieldByIndex([]int) may panic. Indeces map[string][]int // StructFields can be used to look up the reflect.StructField by a generated // key name. // // This field is provided as a convenience so you can map a struct and inspect // field tags without having to use the reflect package yourself. StructFields map[string]reflect.StructField // ReflectPaths can be used to retrieve a path.ReflectPath by mapped name. // // A path.ReflectPath is slightly different and slightly more informative representation // for a path than a plain []int. ReflectPaths map[string]path.ReflectPath // HasPointers will be true if any of the pathways traverse a field that is a pointer. HasPointers bool }
A Mapping is the result of traversing a struct hierarchy to map pathways from the origin (i.e. top-level struct) to fields within the hierarchy.
Mappings are created by calling Mapper.Map(s) where s is the struct you wish to map.
func (Mapping) Get ¶
Get returns the indeces associated with key in the mapping. If no such key is found a nil slice is returned.
type PreparedMapping ¶ added in v0.5.0
type PreparedMapping struct {
// contains filtered or unexported fields
}
PreparedMapping is returned from Mapper's Prepare method.
A PreparedMapping must not be copied except via its Copy method.
PreparedMappings should be used in iterative code that needs to read or mutate many instances of the same struct. PreparedMappings do not allow for indeterminate field access between instances -- every struct instance must have the same fields accessed in the same order. This behavior is akin to prepared statements in a database engine; if you need adhoc or indeterminate access use a BoundMapping.
var a, b T p := myMapper.Prepare(&a) _ = p.Plan("Field", "Other") // check err in production p.Set(10) // a.Field = 10 p.Set("Hello") // a.Other = "Hello" p.Rebind(&b) // resets internal plan counter p.Set(27) // b.Field = 27 p.Set("World") // b.Other = "World"
All methods that return an error will return ErrPlanInvalid until Plan is called specifying an access plan. Methods that do not return an error can be called before a plan has been specified.
func (PreparedMapping) Assignables ¶ added in v0.5.0
func (p PreparedMapping) Assignables(rv []interface{}) ([]interface{}, error)
Assignables returns a slice of pointers to the fields in the currently bound struct in the order specified by the last call to Plan.
To alleviate pressure on the garbage collector the return slice can be pre-allocated and passed as the argument to Assignables. If non-nil it is assumed len(plan) == len(rv) and failure to provide an appropriately sized non-nil slice will cause a panic.
During traversal this method will allocate struct fields that are nil pointers.
An example use-case would be obtaining a slice of pointers for Rows.Scan() during database query results.
func (PreparedMapping) Copy ¶ added in v0.5.0
func (p PreparedMapping) Copy() PreparedMapping
Copy creates an exact copy of the PreparedMapping.
One use case for Copy is to create a set of PreparedMappings early in a program's init phase. During later execution when a PreparedMapping is needed for type T it can be obtained by calling Copy on the cached PreparedMapping for that type.
func (PreparedMapping) Err ¶ added in v0.5.0
func (p PreparedMapping) Err() error
Err returns an error that may have occurred during repeated calls to Set.
Err is reset on calls to Plan or Rebind.
func (*PreparedMapping) Field ¶ added in v0.5.0
func (p *PreparedMapping) Field() (Value, error)
Field returns the Value for the next field.
Each call to Field advances the internal access pointer in order to traverse the fields in the same order as the last call to Plan.
ErrPlanInvalid is returned if Plan has not been called. If this call to Field exceeds the length of the plan then ErrPlanExceeded is returned. Other errors from this package or standard library may also be returned.
func (PreparedMapping) Fields ¶ added in v0.5.0
func (p PreparedMapping) Fields(rv []interface{}) ([]interface{}, error)
Fields returns a slice of values to the fields in the currently bound struct in the order specified by the last call to Plan.
To alleviate pressure on the garbage collector the return slice can be pre-allocated and passed as the argument to Fields. If non-nil it is assumed len(plan) == len(rv) and failure to provide an appropriately sized non-nil slice will cause a panic.
During traversal this method will allocate struct fields that are nil pointers.
An example use-case would be obtaining a slice of query arguments by column name during database queries.
func (*PreparedMapping) Plan ¶ added in v0.5.0
func (p *PreparedMapping) Plan(fields ...string) error
Plan builds the field access plan and must be called before any other methods that return an error.
Each call to plan:
- Resets any internal error to nil
- Resets the internal plan-step counter.
If an unknown field is specified then ErrUnknownField is wrapped with the field name and the internal error is set to ErrPlanInvalid.
func (*PreparedMapping) Rebind ¶ added in v0.5.0
func (p *PreparedMapping) Rebind(v interface{})
Rebind will replace the currently bound value with the new variable v.
v must have the same type as the original value used to create the PreparedMapping otherwise a panic will occur.
As a convenience Rebind allows v to be an instance of reflect.Value. This prevents unnecessary calls to reflect.Value.Interface().
Example (Panic) ¶
Output: mismatching types during Rebind; have *set_test.S and got *set_test.Different
func (*PreparedMapping) Set ¶ added in v0.5.0
func (p *PreparedMapping) Set(value interface{}) error
Set effectively sets V[field] = value.
Each call to Set advances the internal access pointer in order to traverse the fields in the same order as the last call to Plan.
ErrPlanInvalid is returned if Plan has not been called. If this call to Set exceeds the length of the plan then ErrPlanExceeded is returned. Other errors from this package or standard library may also be returned.
type SliceValue ¶ added in v0.5.0
type SliceValue struct { // Top is the original values passed to Slice. Top reflect.Value // V represents []T. V reflect.Value // ElemType and ElemEndType describe the type of elements in the slice. // // ElemType!=reflect.Ptr means ElemType equals ElemEndType; they describe the same type. // ElemType==reflect.Ptr means ElemEndType is the type at the end of the pointer chain. ElemType reflect.Type ElemEndType reflect.Type }
SliceValue wraps around a slice []T and facilitates element creation and appending.
func Slice ¶ added in v0.5.0
func Slice(v interface{}) (SliceValue, error)
Slice expects its argument to be a *[]T or a pointer chain ending in []T.
As a convenience Slice will also accept a reflect.Value as long as it represents a writable []T value.
Example (Errors) ¶
Output: set: Slice: invalid slice: expected pointer to slice; got int set: Slice: invalid slice: expected pointer to slice; got *int set: Slice: invalid slice: expected pointer to slice; got *int set: Slice: read only value: can not set **[]int
func (*SliceValue) Append ¶ added in v0.5.0
func (s *SliceValue) Append(elem reflect.Value)
Append appends an element created by the Elem method.
If the slice is []T then Elem returns a *T. Append automatically dereferences the incoming value so that a T is appended as expected.
func (SliceValue) Elem ¶ added in v0.5.0
func (s SliceValue) Elem() reflect.Value
Elem returns a newly allocated slice element.
If the slice is []T then Elem returns a *T. This is so the created element can be passed directly into function wanting to populate T.
type TypeInfo ¶
type TypeInfo struct { // True if the Value is a scalar type: // bool, float32, float64, string // int, int8, int16, int32, int64 // uint, uint8, uint16, uint32, uint64 IsScalar bool // True if the Value is a map. IsMap bool // True if the Value is a slice. IsSlice bool // True if the Value is a struct. IsStruct bool // Kind is the reflect.Kind; when Stat() or StatType() were called with a pointer this will be the final // kind at the end of the pointer chain. Otherwise it will be the original kind. Kind reflect.Kind // Type is the reflect.Type; when Stat() or StatType() were called with a pointer this will be the final // type at the end of the pointer chain. Otherwise it will be the original type. Type reflect.Type // When IsMap or IsSlice are true then ElemType will be the reflect.Type for elements that can be directly // inserted into the map or slice; it is not the type at the end of the chain if the element type is a pointer. ElemType reflect.Type // When IsStruct is true then StructFields will contain the reflect.StructField values for the struct. StructFields []reflect.StructField }
TypeInfo summarizes information about a type T in a meaningful way for this package.
type TypeInfoCache ¶
type TypeInfoCache interface { // Stat accepts an arbitrary variable and returns the associated TypeInfo structure. Stat(T interface{}) TypeInfo // StatType is the same as Stat() except it expects a reflect.Type. StatType(T reflect.Type) TypeInfo }
TypeInfoCache builds a cache of TypeInfo types; when requesting TypeInfo for a type T that is a pointer the TypeInfo returned will describe the type T' at the end of the pointer chain.
If Stat() or StatType() are called with nil or an Interface(nil) then a zero TypeInfo is returned; essentially nothing useful can be done with the type needed to be described.
func NewTypeInfoCache ¶
func NewTypeInfoCache() TypeInfoCache
NewTypeInfoCache creates a new TypeInfoCache.
type TypeList ¶
TypeList is a list of reflect.Type.
func NewTypeList ¶
func NewTypeList(args ...interface{}) TypeList
NewTypeList creates a new TypeList type from a set of instantiated types.
type Value ¶
type Value struct { // TypeInfo describes the type T in WriteValue. When the value is created with a pointer P // this TypeInfo will describe the final type at the end of the pointer chain. // // To conserve memory and maintain speed this TypeInfo object may be shared with // other Value instances. Altering the members within TypeInfo will most likely // crash your program with a panic. // // Treat this value as read only. TypeInfo // CanWrite specifies if WriteValue.CanSet() would return true. CanWrite bool // TopValue is the original value passed to V() but wrapped in a reflect.Value. TopValue reflect.Value // WriteValue is a reflect.Value representing the modifiable value wrapped within this Value. // // If you call V( &t ) then CanWrite will be true and WriteValue will be a usable reflect.Value. // If you call V( t ) where t is not a pointer or does not point to allocated memory then // CanWrite will be false and any attempt to set values on WriteValue will probably panic. // // All methods on this type that alter the value Append(), Fill*(), To(), etc work on this // value. Generally you should avoid it but it's also present if you really know what you're doing. WriteValue reflect.Value // When IsMap or IsSlice are true then ElemTypeInfo is a TypeInfo struct describing the element type. ElemTypeInfo TypeInfo // contains filtered or unexported fields }
Value wraps around a Go variable and performs magic.
Once created a Value should only be copied via its Copy method.
func V ¶
func V(arg interface{}) Value
V returns a new Value.
The returned Value must not be copied except via its Copy method.
func (Value) Append ¶
Append appends the item(s) to the end of the Value assuming it is some type of slice and every item can be type-coerced into the slice's data type. Either all items are appended without an error or no items are appended and an error is returned describing the type of the item that could not be appended.
func (Value) Copy ¶ added in v0.3.0
Copy creates a clone of the Value and its internal members.
If you need to create many Value for a type T in order to Rebind(T) in a goroutine architecture then consider creating and caching a V(T) early in your application and then calling Copy() on that cached copy before using Rebind().
func (Value) FieldByIndex ¶
FieldByIndex returns the nested field corresponding to index.
Key differences between this method and the built-in method on reflect.Value.FieldByIndex() are the built-in causes panics while this one will return errors and this method will instantiate nil struct members as it traverses.
func (Value) FieldByIndexAsValue ¶
FieldByIndexAsValue calls into FieldByIndex and if there is no error the resulting reflect.Value is wrapped within a call to V() to return a Value.
func (Value) Fields ¶
Fields returns a slice of Field structs when Value is wrapped around a struct; for all other values nil is returned.
This function has some overhead because it creates a new Value for each struct field. If you only need the reflect.StructField information consider using the public StructFields member.
func (Value) FieldsByTag ¶
FieldsByTag is the same as Fields() except only Fields with the given struct-tag are returned and the TagValue member of Field will be set to the tag's value.
func (Value) Fill ¶
Fill iterates a struct's fields and calls To() on each one by passing the field name to the Getter. Fill stops and returns on the first error encountered.
func (Value) FillByTag ¶
FillByTag is the same as Fill() except the argument passed to Getter is the value of the struct-tag.
func (Value) NewElem ¶
NewElem instantiates and returns a Value that can be Panics.Append()'ed to this type; only valid if Value.ElemType describes a valid type.
func (*Value) Rebind ¶
func (v *Value) Rebind(arg interface{})
Rebind will swap the underlying original value used to create Value with the incoming value if:
Type(Original) == Type(Incoming).
If Rebind succeeds the following public members will have been replaced appropriately:
CanWrite TopValue WriteValue
Reach for this function to translate:
var slice []T // populate slice for _, item := range slice { v := set.V( item ) // Creates new Value every iteration -- can be expensive! // manipulate v in order to affect item }
to:
var slice []T v := set.V( T{} ) // Create a single Value for the type T // populate slice for _, item := range slice { v.Rebind( item ) // Reuse the existing Value -- will be faster! // manipulate v in order to affect item }
func (Value) To ¶
To attempts to assign the argument into Value.
If Value is wrapped around an unwritable reflect.Value or the type is reflect.Invalid an error will be returned. You probably forgot to call set.V() with an address to your type.
If the assignment can not be made but the wrapped value is writable then the wrapped value will be set to an appropriate zero type to overwrite any existing data.
set.V(&T).To(S) T is scalar, S is scalar, same type -> direct assignment If S is a pointer then dereference until final S value and continue... T is scalar, S is scalar, different types -> assignment with attempted type coercion T is scalar, S is slice []S -> T is assigned S[ len( S ) - 1 ]; i.e. last element in S if length greater than 0. T is slice []T, S is scalar -> T is set to []T{ S }; i.e. a slice of T with S as the only element. T is slice []T, S is slice []S -> T is set to []T{ S... }; i.e. a new slice with elements from S copied. -> Note: T != S; they are now different slices; changes to T do not affect S and vice versa. -> Note: If the elements themselves are pointers then, for example, T[0] and S[0] point at the same memory and will see changes to whatever is pointed at.
Example ¶
Output: set: Value.To: read only value: bool is not writable: hint=[call to set.V(bool) should have been set.V(*bool)] s 3.14 <nil> n 42 <nil> u8 27 <nil> nptr 100 <nil> sptr Something <nil> s Something f32 3 <nil>
Example (SliceToSlice) ¶
Output: n [9 3 0 9999] <nil> s [9 3.14 false 9999] <nil> n true false n [2 4 6 8] <nil> m [2 -4 6 8] n [2 4 6 8]
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
Package coerce provides loose type coercion and assignment into native Go types.
|
Package coerce provides loose type coercion and assignment into native Go types. |
Package path provides more granular information about paths or pathways from a top-level root struct through its descendents to leaf fields.
|
Package path provides more granular information about paths or pathways from a top-level root struct through its descendents to leaf fields. |