Compare commits
6 Commits
main
...
dev-amery-
Author | SHA1 | Date |
---|---|---|
Alejandro Mery | c94e5e74ad | 1 year ago |
Alejandro Mery | dd252fafae | 1 year ago |
Alejandro Mery | f68272eb84 | 1 year ago |
Alejandro Mery | 1e95acf21d | 1 year ago |
Alejandro Mery | 2b5dcec64d | 1 year ago |
Alejandro Mery | 4aa6233e4f | 1 year ago |
7 changed files with 296 additions and 8 deletions
@ -0,0 +1,69 @@
|
||||
package reflection |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"reflect" |
||||
) |
||||
|
||||
// An InvalidUnmarshalError describes an invalid argument passed to New.
|
||||
// (It must be a non-nil pointer.)
|
||||
type InvalidUnmarshalError struct { |
||||
Method string |
||||
Prefix string |
||||
|
||||
reflect.Type |
||||
} |
||||
|
||||
func (e *InvalidUnmarshalError) Error() string { |
||||
var buf bytes.Buffer |
||||
|
||||
if e.Prefix != "" { |
||||
_, _ = fmt.Fprintf(&buf, "%s: ", e.Prefix) |
||||
} |
||||
|
||||
method := e.Method |
||||
if method == "" { |
||||
method = "New" |
||||
} |
||||
|
||||
_, _ = fmt.Fprintf(&buf, "%s(", method) |
||||
|
||||
switch { |
||||
case e.Type == nil: |
||||
_, _ = buf.WriteString("nil") |
||||
case e.Type.Kind() != reflect.Pointer: |
||||
_, _ = fmt.Fprintf(&buf, "%s %s", "non-pointer", e.Type.String()) |
||||
default: |
||||
_, _ = fmt.Fprintf(&buf, "%s %s", "nil", e.Type.String()) |
||||
} |
||||
|
||||
_, _ = buf.WriteString(")") |
||||
|
||||
return buf.String() |
||||
} |
||||
|
||||
// An UnmarshalTypeError tells something went wrong while processing
|
||||
// a type.
|
||||
type UnmarshalTypeError struct { |
||||
Prefix string |
||||
Err error |
||||
|
||||
reflect.Type |
||||
} |
||||
|
||||
func (e UnmarshalTypeError) Unwrap() error { |
||||
return e.Err |
||||
} |
||||
|
||||
func (e UnmarshalTypeError) Error() string { |
||||
var buf bytes.Buffer |
||||
|
||||
if e.Prefix != "" { |
||||
_, _ = fmt.Fprintf(&buf, "%s: ", e.Prefix) |
||||
} |
||||
|
||||
_, _ = fmt.Fprintf(&buf, "%s: %s", e.Type.String(), e.Err) |
||||
|
||||
return buf.String() |
||||
} |
@ -0,0 +1,16 @@
|
||||
package reflection |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
) |
||||
|
||||
func structFieldName(sf reflect.StructField) string { |
||||
if sf.Anonymous { |
||||
idx := sf.Index[len(sf.Index)-1] |
||||
|
||||
return fmt.Sprintf("%s-%v", "anonymous", idx) |
||||
} |
||||
|
||||
return sf.Name |
||||
} |
@ -0,0 +1,40 @@
|
||||
// Package reflection helps Marshalling/Unmarshalling data to structs
|
||||
package reflection |
||||
|
||||
import ( |
||||
"reflect" |
||||
) |
||||
|
||||
// Reflection provides Marshalling/Unmarshalling oriented view
|
||||
// of a value
|
||||
type Reflection struct { |
||||
v reflect.Value |
||||
|
||||
types map[reflect.Type]TypeInfo |
||||
} |
||||
|
||||
// Value returns the object it reflects
|
||||
func (r *Reflection) Value() any { |
||||
return r.v.Interface() |
||||
} |
||||
|
||||
// New creates a Reflection of the given pointer
|
||||
func New(v any) (*Reflection, error) { |
||||
rv := reflect.ValueOf(v) |
||||
|
||||
if rv.Kind() != reflect.Pointer || rv.IsNil() { |
||||
err := &InvalidUnmarshalError{Type: rv.Type()} |
||||
return nil, err |
||||
} |
||||
|
||||
r := &Reflection{ |
||||
v: rv, |
||||
types: make(map[reflect.Type]TypeInfo), |
||||
} |
||||
|
||||
if err := r.scan(); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return r, nil |
||||
} |
@ -0,0 +1,37 @@
|
||||
package reflection |
||||
|
||||
import ( |
||||
"reflect" |
||||
) |
||||
|
||||
func (r *Reflection) scanType(rt reflect.Type) error { |
||||
switch rt.Kind() { |
||||
case reflect.Pointer, reflect.Slice: |
||||
rt = rt.Elem() |
||||
} |
||||
|
||||
if _, ok := r.types[rt]; ok { |
||||
// cached
|
||||
return nil |
||||
} |
||||
|
||||
ti, err := NewTypeInfo(rt) |
||||
if err != nil { |
||||
return &UnmarshalTypeError{Type: rt, Err: err} |
||||
} |
||||
|
||||
r.types[rt] = ti |
||||
|
||||
for i := range ti.fields { |
||||
err := r.scanType(ti.fields[i].sf.Type) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (r *Reflection) scan() error { |
||||
return r.scanType(r.v.Type()) |
||||
} |
@ -0,0 +1,108 @@
|
||||
package reflection |
||||
|
||||
import ( |
||||
"log" |
||||
"reflect" |
||||
|
||||
"github.com/fatih/structtag" |
||||
"github.com/pkg/errors" |
||||
) |
||||
|
||||
// TypeInfo ...
|
||||
type TypeInfo struct { |
||||
rt reflect.Type |
||||
|
||||
fields []TypeInfoField |
||||
} |
||||
|
||||
// Type ...
|
||||
func (ti *TypeInfo) Type() reflect.Type { |
||||
return ti.rt |
||||
} |
||||
|
||||
// Kind ...
|
||||
func (ti *TypeInfo) Kind() reflect.Kind { |
||||
return ti.rt.Kind() |
||||
} |
||||
|
||||
// TypeInfoField ...
|
||||
type TypeInfoField struct { |
||||
sf reflect.StructField |
||||
tags *structtag.Tags |
||||
|
||||
Name string |
||||
} |
||||
|
||||
// Type ...
|
||||
func (tif *TypeInfoField) Type() reflect.Type { |
||||
return tif.sf.Type |
||||
} |
||||
|
||||
// Kind ...
|
||||
func (tif *TypeInfoField) Kind() reflect.Kind { |
||||
return tif.sf.Type.Kind() |
||||
} |
||||
|
||||
// Tag ...
|
||||
func (tif *TypeInfoField) Tag(key string) (*structtag.Tag, bool) { |
||||
if tif.tags != nil { |
||||
tag, _ := tif.tags.Get(key) |
||||
if tag != nil { |
||||
return tag, true |
||||
} |
||||
} |
||||
return nil, false |
||||
} |
||||
|
||||
// NewTypeInfo ...
|
||||
func NewTypeInfo(rt reflect.Type) (TypeInfo, error) { |
||||
log.Printf("%s.%s: %s (%s)", "reflection", "NewTypeInfo", rt, rt.Kind()) |
||||
|
||||
ti := TypeInfo{ |
||||
rt: rt, |
||||
} |
||||
|
||||
err := ti.init() |
||||
return ti, err |
||||
} |
||||
|
||||
func (ti *TypeInfo) init() error { |
||||
switch ti.Kind() { |
||||
case reflect.Struct: |
||||
return ti.initStruct() |
||||
default: |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
func (ti *TypeInfo) initStruct() error { |
||||
// load fields
|
||||
n := ti.rt.NumField() |
||||
fields := make([]TypeInfoField, 0, n) |
||||
for i := 0; i < n; i++ { |
||||
sf := ti.rt.Field(i) |
||||
tags, err := structtag.Parse(string(sf.Tag)) |
||||
|
||||
if err != nil { |
||||
// tag parse error
|
||||
err = &UnmarshalTypeError{ |
||||
Type: ti.rt, |
||||
Err: errors.Wrap(err, structFieldName(sf)), |
||||
} |
||||
return err |
||||
} |
||||
|
||||
if sf.IsExported() { |
||||
log.Printf("%s.%s: [%v]: %q %s (%s)", |
||||
"reflection", "NewTypeInfo", |
||||
i, structFieldName(sf), sf.Type, sf.Type.Kind()) |
||||
|
||||
fields = append(fields, TypeInfoField{ |
||||
sf: sf, |
||||
tags: tags, |
||||
}) |
||||
} |
||||
} |
||||
ti.fields = fields |
||||
return nil |
||||
} |
Loading…
Reference in new issue