1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

[structs] change package structutil to structs (#81)

* refactor package structutil to structs

* add structs package zh-CN docs
This commit is contained in:
zm
2023-03-15 19:14:19 +08:00
committed by GitHub
parent 7261b281ad
commit 5e66bc6227
12 changed files with 594 additions and 26 deletions

View File

@@ -11,7 +11,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/duke-git/lancet/v2/structutil"
"github.com/duke-git/lancet/v2/structs"
"math"
"reflect"
"strconv"
@@ -235,7 +235,7 @@ func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K
// map key is specified same as struct field tag `json` value.
// Play: https://go.dev/play/p/KYGYJqNUBOI
func StructToMap(value any) (map[string]any, error) {
return structutil.ToMap(value)
return structs.ToMap(value)
}
// MapToSlice convert map to slice based on iteratee function.

353
docs/structs/field_zh-CN.md Normal file
View File

@@ -0,0 +1,353 @@
# Field
Field 包封装了一个抽象的`Field`结构体,提供了操作`struct`属性的相关函数
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/structs/field.go](https://github.com/duke-git/lancet/blob/main/structs/field.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/structs"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录:
- [Tag](#Tag)
- [Name](#Name)
- [Value](#Value)
- [Kind](#Kind)
- [IsEmbedded](#IsEmbedded)
- [IsExported](#IsExported)
- [IsZero](#IsZero)
- [IsSlice](#IsSlice)
> 注意:由于`Field`继承于`Struct`,所以同样拥有`Struct`所有方法,如下:
- [ToMap](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#ToMap)
- [Fields](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN#Fields)
- [Field](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN#Field)
- [IsStruct](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN#IsStruct)
<div STYLE="page-break-after: always;"></div>
## API 文档:
### <span id="Tag">Tag</span>
<p>获取`Field``Tag`默认的tag key是json</p>
<b>函数签名:</b>
```go
func (f *Field) Tag() *Tag
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string `json:"name,omitempty"`
}
p1 := &Parent{"111"}
s := structs.New(p1)
n, _ := s.Field("Name")
tag := n.Tag()
fmt.Println(tag.Name)
// Output:
// name
}
```
### <span id="Value">Value</span>
<p>获取`Field`属性的值</p>
<b>函数签名:</b>
```go
func (f *Field) Value() any
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string `json:"name,omitempty"`
}
p1 := &Parent{"111"}
s := structs.New(p1)
n, _ := s.Field("Name")
fmt.Println(n.Value())
// Output:
// 111
}
```
### <span id="IsEmbedded">IsEmbedded</span>
<p>判断属性是否为嵌入</p>
<b>函数签名:</b>
```go
func (f *Field) IsEmbedded() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
}
type Child struct {
Parent
Age int
}
c1 := &Child{}
c1.Name = "111"
c1.Age = 11
s := structs.New(c1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.IsEmbedded())
fmt.Println(a.IsEmbedded())
// Output:
// true
// false
}
```
### <span id="IsExported">IsExported</span>
<p>判断属性是否导出</p>
<b>函数签名:</b>
```go
func (f *Field) IsExported() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
age int
}
p1 := &Parent{Name: "11", age: 11}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("age")
fmt.Println(n.IsExported())
fmt.Println(a.IsExported())
// Output:
// true
// false
}
```
### <span id="IsZero">IsZero</span>
<p>判断属性是否为零值</p>
<b>函数签名:</b>
```go
func (f *Field) IsZero() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
Age int
}
p1 := &Parent{Age: 11}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.IsZero())
fmt.Println(a.IsZero())
// Output:
// true
// false
}
```
### <span id="Name">Name</span>
<p>获取属性名</p>
<b>函数签名:</b>
```go
func (f *Field) Name() string
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
Age int
}
p1 := &Parent{Age: 11}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.Name())
fmt.Println(a.Name())
// Output:
// Name
// Age
}
```
### <span id="Kind">Kind</span>
<p>获取属性Kind</p>
<b>函数签名:</b>
```go
func (f *Field) Kind() reflect.Kind
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
Age int
}
p1 := &Parent{Age: 11}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("Age")
fmt.Println(n.Kind())
fmt.Println(a.Kind())
// Output:
// string
// int
}
```
### <span id="IsSlice">IsSlice</span>
<p>判断属性是否是切片</p>
<b>函数签名:</b>
```go
func (f *Field) IsSlice() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
arr []int
}
p1 := &Parent{arr: []int{1, 2, 3}}
s := structs.New(p1)
a, _ := s.Field("arr")
fmt.Println(a.IsSlice())
// Output:
// true
}
```

2
docs/structs/struct.md Normal file
View File

@@ -0,0 +1,2 @@
# Structs

View File

@@ -0,0 +1,212 @@
# Structs
structs 包封装了一个抽象的`Struct`结构体,提供了操作`struct`的相关函数
<div STYLE="page-break-after: always;"></div>
## 源码:
- [https://github.com/duke-git/lancet/blob/main/structs/struct.go](https://github.com/duke-git/lancet/blob/main/structs/struct.go)
<div STYLE="page-break-after: always;"></div>
## 用法:
```go
import (
"github.com/duke-git/lancet/v2/structs"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录:
- [New](#New)
- [ToMap](#ToMap)
- [Fields](#Fields)
- [Field](#Field)
- [IsStruct](#IsStruct)
<div STYLE="page-break-after: always;"></div>
## API 文档:
### <span id="New">New</span>
<p>`Struct`结构体的构造函数</p>
<b>函数签名:</b>
```go
func New(value any, tagName ...string) *Struct
```
<b>示例:</b>
```go
package main
import (
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type People struct {
Name string `json:"name"`
}
p1 := &People{Name: "11"}
s := structs.New(p1)
// to do something
}
```
### <span id="ToMap">ToMap</span>
<p>将一个合法的struct对象转换为map[string]any</p>
<b>函数签名:</b>
```go
func (s *Struct) ToMap() (map[string]any, error)
```
<b>除此之外提供一个便捷的静态方法ToMap</b>
```go
func ToMap(v any) (map[string]any, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type People struct {
Name string `json:"name"`
}
p1 := &People{Name: "11"}
s1 := structs.New(p1)
m1, _ := s1.ToMap()
fmt.Println(m1)
// 如果不需要Struct更多的方法可以直接使用ToMap
m2, _ := structs.ToMap(p1)
fmt.Println(m2)
// Output:
// map[name:11]
// map[name:11]
}
```
### <span id="Fields">Fields</span>
<p>获取一个struct对象的属性列表</p>
<b>函数签名:</b>
```go
func (s *Struct) Fields() []*Field
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type People struct {
Name string `json:"name"`
}
p1 := &People{Name: "11"}
s := structs.New(p1)
fields := s.Fields()
fmt.Println(len(fields))
// Output:
// 1
}
```
### <span id="Field">Field</span>
<p>根据属性名获取一个struct对象的属性</p>
<b>函数签名:</b>
```go
func (s *Struct) Field(name string) *Field
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type People struct {
Name string `json:"name"`
}
p1 := &People{Name: "11"}
s := structs.New(p1)
f := s.Field("Name")
fmt.Println(f.Value())
// Output:
// 11
}
```
### <span id="IsStruct">IsStruct</span>
<p>判断是否为一个合法的struct对象</p>
<b>函数签名:</b>
```go
func (s *Struct) IsStruct() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type People struct {
Name string `json:"name"`
}
p1 := &People{Name: "11"}
s := structs.New(p1)
fmt.Println(s.IsStruct())
// Output:
// true
}
```

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import (
"reflect"
@@ -20,6 +20,7 @@ func newField(v reflect.Value, f reflect.StructField, tagName string) *Field {
tag: newTag(tag),
}
field.rvalue = v
field.rtype = v.Type()
field.TagName = tagName
return field
}
@@ -67,8 +68,8 @@ func (f *Field) IsSlice() bool {
return k == reflect.Slice
}
// MapValue conver field value to map.
func (f *Field) MapValue(value any) any {
// mapValue covert field value to map
func (f *Field) mapValue(value any) any {
val := pointer.ExtractPointer(value)
v := reflect.ValueOf(val)
var ret any
@@ -86,7 +87,7 @@ func (f *Field) MapValue(value any) any {
// iterate the map
m := make(map[string]any, v.Len())
for _, key := range v.MapKeys() {
m[key.String()] = f.MapValue(v.MapIndex(key).Interface())
m[key.String()] = f.mapValue(v.MapIndex(key).Interface())
}
ret = m
default:
@@ -98,7 +99,7 @@ func (f *Field) MapValue(value any) any {
case reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice, reflect.Chan:
slices := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
slices[i] = f.MapValue(v.Index(i).Interface())
slices[i] = f.mapValue(v.Index(i).Interface())
}
ret = slices
default:

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import (
"github.com/duke-git/lancet/v2/internal"
@@ -152,7 +152,7 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
assert.Equal(map[string]any{"name": "11-1"}, val)
@@ -176,7 +176,7 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
assert.Equal(map[string]any{"name": "11-1"}, val)
@@ -195,13 +195,13 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
assert.Equal([]int{1, 2, 3}, val)
})
t.Run("nested array in struct", func(t *testing.T) {
t.Run("nested array struct", func(t *testing.T) {
type Child struct {
Name string `json:"name"`
}
@@ -220,14 +220,14 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}}
assert.Equal(arr, val)
})
t.Run("nested ptr array in struct", func(t *testing.T) {
t.Run("nested ptr array struct", func(t *testing.T) {
type Child struct {
Name string `json:"name"`
}
@@ -246,7 +246,7 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}}
@@ -265,7 +265,7 @@ func TestField_MapValue(t *testing.T) {
s := New(p1)
f, ok := s.Field("Child")
val := f.MapValue(f.Value())
val := f.mapValue(f.Value())
assert.Equal(true, ok)
assert.Equal(map[string]any{"a": 1, "b": map[string]any{"name": "11-1"}}, val)
})

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import (
"reflect"
@@ -6,7 +6,7 @@ import (
"github.com/duke-git/lancet/v2/pointer"
)
// DefaultTagName is the default tag for struct fields to lookup.
// defaultTagName is the default tag for struct fields to lookup.
var defaultTagName = "json"
// Struct is abstract struct for provide several high level functions
@@ -29,7 +29,7 @@ func New(value any, tagName ...string) *Struct {
tn = tagName[0]
}
// if need: can also set defaultTagName to tn across structutil package level
// if need: can also set defaultTagName to tn across structs package level
// defaultTagName = tn
return &Struct{
@@ -57,7 +57,7 @@ func New(value any, tagName ...string) *Struct {
// // custom map key
// Name string `json:"myName"`
//
// ToMap conver the exported fields of a struct to map.
// ToMap convert the exported fields of a struct to map.
func (s *Struct) ToMap() (map[string]any, error) {
if !s.IsStruct() {
return nil, errInvalidStruct(s)
@@ -72,7 +72,7 @@ func (s *Struct) ToMap() (map[string]any, error) {
if f.IsZero() && f.tag.HasOption("omitempty") {
continue
}
result[f.tag.Name] = f.MapValue(f.Value())
result[f.tag.Name] = f.mapValue(f.Value())
}
return result, nil

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import "fmt"

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import (
"reflect"
@@ -10,7 +10,7 @@ import (
func TestStruct_ToMap(t *testing.T) {
assert := internal.NewAssert(t, "TestStruct_ToMap")
t.Run("no struct", func(t *testing.T) {
t.Run("invalid struct", func(t *testing.T) {
m, _ := ToMap(1)
var expected map[string]any
assert.Equal(expected, m)
@@ -21,7 +21,7 @@ func TestStruct_ToMap(t *testing.T) {
Name string `json:"name"`
age int
}
p := People{
p := &People{
"test",
100,
}

View File

@@ -1,4 +1,4 @@
package structutil
package structs
import (
"strings"