Documentation ¶
Overview ¶
Example (Compare) ¶
package main import ( "fmt" "reflect" "sort" "strings" "github.com/future-architect/tagscanner/runtimescan" ) type cmpStrategy struct { Path string IgnoreCase bool } type compare struct { values map[string]any } func (c compare) ParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error) { if tagStr == "skip" { return nil, runtimescan.Skip } if tagStr == "ignorecase" && elemType.Kind() != reflect.String { return nil, fmt.Errorf("the field '%v' is specified as 'ignorecase' but it is not string.", pathStr) } return &cmpStrategy{ Path: pathStr, IgnoreCase: tagStr == "ignorecase", }, nil } func (c *compare) VisitField(tag, value any) (err error) { t := tag.(*cmpStrategy) if t.IgnoreCase { c.values[t.Path] = strings.ToLower(value.(string)) } else { c.values[t.Path] = value } return nil } func (c compare) EnterChild(tag any) (err error) { return nil } func (c compare) LeaveChild(tag any) (err error) { return nil } func Compare(s1, s2 any) (bool, []string, error) { c1 := compare{ values: make(map[string]any), } err := runtimescan.Encode(s1, []string{"cmp"}, &c1) if err != nil { return false, nil, err } c2 := compare{ values: make(map[string]any), } err = runtimescan.Encode(s2, []string{"cmp"}, &c2) if err != nil { return false, nil, err } var unmatch []string for k, v1 := range c1.values { if c2.values[k] != v1 { unmatch = append(unmatch, k) } } for k, v2 := range c2.values { v1, ok := c1.values[k] if !ok && v1 != v2 { unmatch = append(unmatch, k) } } sort.Strings(unmatch) return len(unmatch) == 0, unmatch, nil } func main() { s1 := struct { BothHaveSameValue int DifferentValue int DifferentType int Skip1 string `cmp:"skip"` IgnoreCase string `cmp:"ignorecase"` OnlyOnS1 bool }{ BothHaveSameValue: 17, DifferentValue: 19, DifferentType: 11, Skip1: "skip by tag", IgnoreCase: "", OnlyOnS1: true, } s2 := struct { BothHaveSameValue int DifferentValue int DifferentType float64 Skip2 string `cmp:"skip"` IgnoreCase string `cmp:"ignorecase"` OnlyOnS2 bool }{ BothHaveSameValue: 17, DifferentValue: 23, DifferentType: 1.23, Skip2: "skip by tag", IgnoreCase: "", OnlyOnS2: true, } equal, diffs, err := Compare(&s1, &s2) fmt.Printf("equal: %v\n", equal) fmt.Printf("different keys: %v\n", diffs) fmt.Printf("err: %v\n", err) }
Output: equal: false different keys: [DifferentType DifferentValue OnlyOnS1 OnlyOnS2] err: <nil>
Example (Copy) ¶
package main import ( "fmt" "log" "reflect" "github.com/future-architect/tagscanner/runtimescan" ) type cpyStrategy struct { Path string } type cpy struct { values map[string]any } func (c *cpy) VisitField(tag, value any) (err error) { t := tag.(*cpyStrategy) c.values[t.Path] = value return nil } func (c cpy) EnterChild(tag any) (err error) { return nil } func (c cpy) LeaveChild(tag any) (err error) { return nil } func (c cpy) ExtractValue(tag any) (value any, err error) { t := tag.(*cpyStrategy) if v, ok := c.values[t.Path]; ok { return v, nil } return nil, runtimescan.Skip } func (c cpy) ParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error) { if tagStr == "skip" { return nil, runtimescan.Skip } return &cpyStrategy{ Path: pathStr, }, nil } func Copy(dest, src any) error { c := &cpy{ values: make(map[string]any), } err := runtimescan.Encode(src, []string{"copy"}, c) if err != nil { return err } return runtimescan.Decode(dest, []string{"copy"}, c) } type Struct struct { Value string Ignore string `copy:"skip"` } func main() { src := Struct{ Value: "copy from source", Ignore: "this value should be ignored", } dest := Struct{} err := Copy(&dest, &src) if err != nil { log.Fatal(err) } fmt.Printf("Value: %s\n", dest.Value) fmt.Printf("Ignore: %s\n", dest.Ignore) }
Output: Value: copy from source Ignore:
Example (Map2struct) ¶
package main import ( "fmt" "log" "reflect" "github.com/future-architect/tagscanner/runtimescan" ) // User should implement runtimescan.Decoder interface // This instance is created in user code before runtimescan.Decode() function call type decoder struct { src map[string]any } func (m decoder) ParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error) { return runtimescan.BasicParseTag(name, tagKey, tagStr, pathStr, elemType) } func (m *decoder) ExtractValue(tag any) (value any, err error) { t := tag.(*runtimescan.BasicTag) v, ok := m.src[t.Tag] if !ok { return nil, runtimescan.Skip } return v, nil } func Decode(dest any, src map[string]any) error { dec := &decoder{ src: src, } return runtimescan.Decode(dest, []string{"map"}, dec) } func main() { sampleMap := map[string]any{ "int": 1, "float": 1.1, "string": "hello world", "private": "this should be ignored", } sampleStruct := struct { Int int `map:"int"` Pointer *float64 `map:"float"` String string NonExisting *bool `map:"bool"` private string `map:"private"` }{} err := Decode(&sampleStruct, sampleMap) if err != nil { log.Fatalln(err) } fmt.Printf("Field with tag: %v\n", sampleStruct.Int) fmt.Printf("Pointer field with tag: %v\n", *(sampleStruct.Pointer)) fmt.Printf("Field without tag: %v\n", sampleStruct.String) fmt.Printf("Field that doesn't exist in source: %v\n", sampleStruct.NonExisting) fmt.Printf("Private field is ignored: %v\n", sampleStruct.private) }
Output: Field with tag: 1 Pointer field with tag: 1.1 Field without tag: hello world Field that doesn't exist in source: <nil> Private field is ignored:
Example (Struct2map) ¶
package main import ( "fmt" "log" "reflect" "github.com/future-architect/tagscanner/runtimescan" ) type encoder struct { dest map[string]any } func (m encoder) ParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error) { return runtimescan.BasicParseTag(name, tagKey, tagStr, pathStr, elemType) } func (m *encoder) VisitField(tag, value any) (err error) { t := tag.(*runtimescan.BasicTag) m.dest[t.Tag] = value return nil } func (m encoder) EnterChild(tag any) (err error) { return nil } func (m encoder) LeaveChild(tag any) (err error) { return nil } func Encode(dest map[string]any, src any) error { enc := &encoder{ dest: dest, } return runtimescan.Encode(src, []string{"map"}, enc) } func main() { destMap := map[string]any{} sampleStruct := struct { Int int `map:"int"` Pointer *float64 `map:"float"` String string NonExisting *bool `map:"bool"` private string `map:"private"` }{ Int: 13, Pointer: &[]float64{3.1415}[0], String: "hello world", private: "this should be ignored", } err := Encode(destMap, &sampleStruct) if err != nil { log.Fatalln(err) } fmt.Printf("string: %v\n", destMap["string"]) fmt.Printf("int: %v\n", destMap["int"]) fmt.Printf("float: %v\n", destMap["float"]) }
Output: string: hello world int: 13 float: 3.1415
Index ¶
- Variables
- func BasicParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error)
- func Decode(dest any, tags []string, decoder Decoder) error
- func Encode(src any, tags []string, encoder Encoder) error
- func FuzzyAssign(dest, value any) error
- func IsPointerOfSliceOfPointerOfStruct(i any) bool
- func IsPointerOfSliceOfStruct(i any) bool
- func IsPointerOfStruct(i any) bool
- func NewStructInstance(i any) (any, error)
- func Str2PrimitiveValue(value string, elemType reflect.Type) (any, error)
- type BasicTag
- type Decoder
- type Encoder
- type Errors
- type Parser
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // SkipTraverse is returned by Parser interface's ParseTag() method to notify to skip traversing child struct. SkipTraverse = errors.New("skip traverse") ErrParseTag = errors.New("tag parse error") // Skip is a flag to skip. This is returned by Parser interface's ParseTag() method to notify to add skip tag // and ExtractValue() method of Decoder interface. Skip = errors.New("skip") )
var Epsilon = 0.001
Epsilon is a threshold value that specify input floating point value is true or false when converting from float64 to bool by using FuzzyAssign().
var ErrAssignError = errors.New("assign error")
ErrAssignError is a base error that is happens in FuzzyAssign().
Functions ¶
func BasicParseTag ¶
func BasicParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error)
BasicParseTag is a helper function to make tagscanner easy.
Both Encoder and Decoder should implement ParseTag() method. This is the simplest implementation of these methods.
func FuzzyAssign ¶
FuzzyAssign assigns value to variable. It converts data format to meet variable type as much as possible.
func IsPointerOfSliceOfPointerOfStruct ¶
IsPointerOfSliceOfPointerOfStruct is a helper function that checks passed any is the form of *[]*struct.
func IsPointerOfSliceOfStruct ¶
IsPointerOfSliceOfStruct is a helper function that checks passed any is the form of *[]struct.
func IsPointerOfStruct ¶
IsPointerOfStruct is a helper function that checks passed any is the form of *struct.
func NewStructInstance ¶
NewStructInstance is a helper function that returns new instance based on passed input.
Even if the input has a form of *struct, *[]struct, *[]*struct, it creates new instance and returns in the form of *struct.
Types ¶
type Decoder ¶
type Decoder interface { Parser // ExtractValue is called for each field of each struct instance inside Decode(). ExtractValue(tag any) (value any, err error) }
Decoder is an interface that extracts value from some type and assign to struct instance.
ParseTag() is used when parsing struct tag.
type Encoder ¶
type Encoder interface { Parser VisitField(tag, value any) (err error) EnterChild(tag any) (err error) LeaveChild(tag any) (err error) }
Encoder is an interface that receive values from some type and create other files.
ParseTag() is used when parsing struct tag.
VisitField() is called when getting value from source struct. EnterChild() and LeaveChild)() are called when traversing struct structure.
type Parser ¶
type Parser interface { // ParseTag is called when parsing struct. This is called once per each struct definition // inside Decode() or Encode() // Returned value of this method is passed to ExtractValue() method call. // // name: Field name // tagKey: Tag key // tagStr: Tag string // pathStr: Field name for error message (it contains nested struct names) // elemType: Field type ParseTag(name, tagKey, tagStr, pathStr string, elemType reflect.Type) (tag any, err error) }
Parser is a part of Decoder and Encoder. The user of this library will implement this method.