Skip to content

Commit

Permalink
[+] chore: beta/exporter - done struct in struct
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew-M-C committed Feb 20, 2023
1 parent 51353f6 commit c825784
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
2 changes: 2 additions & 0 deletions beta/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"

jsonvalue "github.com/Andrew-M-C/go.jsonvalue"
// "github.com/Andrew-M-C/go.util/runtime/caller"
)

type any = interface{}
Expand Down Expand Up @@ -139,6 +140,7 @@ func (e *baseExporter) checkInputType(v any) (reflect.Value, bool) {
ok := e.typ == typ
if !ok {
internal.debugf("exporter type '%v', but given '%v'", e.typ, typ)
// internal.debugf("caller: %v", caller.GetCaller(2))
return reflect.Value{}, false
}
return reflect.ValueOf(v), ok
Expand Down
8 changes: 4 additions & 4 deletions beta/exporter/exporter_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@ import (
type g struct {
lock sync.RWMutex

exportersByType map[reflect.Type]Exporter
exportersByType map[reflect.Type]omnipotentExporter

debugf func(string, ...any)
}

var internal = &g{}

func init() {
internal.exportersByType = make(map[reflect.Type]Exporter)
internal.exportersByType = make(map[reflect.Type]omnipotentExporter)
internal.debugf = func(string, ...any) {}
}

func (i *g) loadExportersByType(typ reflect.Type) (Exporter, bool) {
func (i *g) loadExportersByType(typ reflect.Type) (omnipotentExporter, bool) {
i.lock.RLock()
defer i.lock.RUnlock()

e, exist := i.exportersByType[typ]
return e, exist
}

func (i *g) storeExporterByType(typ reflect.Type, e Exporter) {
func (i *g) storeExporterByType(typ reflect.Type, e omnipotentExporter) {
internal.lock.Lock()
defer internal.lock.Unlock()

Expand Down
81 changes: 74 additions & 7 deletions beta/exporter/exporter_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type structExporter struct {

fields []*structField

exportersByType map[reflect.Type]structFieldExporter // 这个类型主要用于无锁调用
exportersByType map[reflect.Type]structFieldExporter // 内部引用其他类型的 exporter
typesToAnalyze map[reflect.Type]struct{} // 仅在初始化过程中使用, 当遇到了未处理的 struct 类型时, 则继续处理
}

Expand Down Expand Up @@ -52,7 +52,7 @@ func (e *structExporter) Export(rv any) *jsonvalue.V {

// exportField implements structFieldExporter interface
func (e *structExporter) exportField(field reflect.Value) (fieldV *jsonvalue.V, valid bool) {
fieldV = e.Export(field)
fieldV = e.Export(field.Interface())
return fieldV, fieldV.Len() > 0
}

Expand All @@ -75,6 +75,12 @@ func (e *structExporter) markTypeToParse(typ reflect.Type) {
}
}

func (e *structExporter) storeExporter(t reflect.Type, exp omnipotentExporter) {
delete(e.typesToAnalyze, t)
e.exportersByType[t] = exp
internal.debugf("%v - type analyzed: '%v'", e.typ, t)
}

type structField struct {
typ reflect.Type
key string
Expand Down Expand Up @@ -102,8 +108,9 @@ func parseStructExporter(typ reflect.Type) *structExporter {
typ: {},
}

e.parse()
e.exportersByType[e.typ] = e
e.parse()

return e
}

Expand All @@ -118,6 +125,7 @@ func (e *structExporter) parse() {

// TODO: 处理 typesToAnalyze
internal.debugf("%v: remaining struct to analyze: %+v", e.typ, e.typesToAnalyze)

for len(e.typesToAnalyze) > 0 {
// get one
var t reflect.Type
Expand All @@ -127,24 +135,63 @@ func (e *structExporter) parse() {
}

internal.debugf("%v - now parse type '%v'", e.typ, t)
if exp, exist := internal.loadExportersByType(t); exist {
internal.debugf("%v - found type in cache: '%v'", e.typ, t)
e.storeExporter(t, exp)
continue
}

// 解析类型
switch t.Kind() {
default:
// do nothing

case reflect.Array:
// TODO:

case reflect.Interface:
// TODO:

case reflect.Map:
// TODO:

case reflect.Slice:
// TODO:

case reflect.Struct:
// TODO: FIXME: 有 bug, 递归了
exporter := e.parseNotSelfStruct(t)
e.storeExporter(t, exporter)

case reflect.Pointer:
// 检查一下是不是 struct pointer
elemType := t.Elem()
if elemType == e.typ { // 如果是自己的话, 那么直接取
elemE := &pointerExporter{}
elemE.typ = t
elemE.elemExporter = e
e.exportersByType[t] = elemE
e.storeExporter(t, elemE)
// internal.storeExporterByType(e.typ, elemE)
internal.debugf("%v - add exporter for type %v", e.typ, t)
} else {
// TODO: 如果不是自己的话, 那么就重新取

} else { // 如果不是自己的话, 那么就重新取
var exporter omnipotentExporter
var err error
if elemType.Kind() == reflect.Struct {
exporter = e.parseNotSelfStruct(elemType) // struct 类型的逻辑与前面相同, 避免嵌套
} else {
exporter, err = validateTypeAndReturnExporter(elemType)
}
if err != nil {
internal.debugf("%v - ERROR: parse type '%v' error: %v", e.typ, elemType, err)
} else {
elemE := &pointerExporter{}
elemE.typ = t
elemE.elemExporter = exporter
e.storeExporter(t, elemE)
// internal.storeExporterByType(e.typ, elemE)
internal.debugf("%v - add exporter for type %v", e.typ, t)
}
}
}

Expand Down Expand Up @@ -204,7 +251,7 @@ func (e *structExporter) parseStructFieldExporter(
internal.debugf("%v: skip field type %v", ft.Type, elemType)
return
}
e.markTypeToParse(ft.Type) // FIXME: 这里不对!!
e.markTypeToParse(ft.Type)

case reflect.Slice:
// TODO:
Expand Down Expand Up @@ -296,6 +343,26 @@ func (e *structExporter) parseStructAnonymousFieldExporter(
return
}

// 解析 struct 类型成员, 但不是自己。
//
// 针对 struct in struct, 需要再创建一个 *structExporter, 但它的 exportersByType 和
// typesToAnalyze 可以取 parent 的, 以避免循环嵌套
func (e *structExporter) parseNotSelfStruct(t reflect.Type) omnipotentExporter {
subE := &structExporter{}
subE.typ = t
subE.exportersByType = e.exportersByType
subE.typesToAnalyze = e.typesToAnalyze

for i := 0; i < t.NumField(); i++ {
tt := t.Field(i)

fields := subE.parseStructFieldExporter(i, tt)
subE.fields = append(subE.fields, fields...)
}

return subE
}

func readFieldTag(ft reflect.StructField, name string) (field string, ex structFieldExt) {
tg := ft.Tag.Get(name)

Expand Down
51 changes: 41 additions & 10 deletions beta/exporter/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ func TestExporter(t *testing.T) {

cv("最基本的 struct", t, func() { testSimplestStruct(t) })
cv("struct 嵌套自己", t, func() { testNestedStruct(t) })
}

type simplestStruct struct {
S string `json:"s"`
I int `json:"i"`
cv("struct 嵌套别的 struct", t, func() { testStructNestingOtherStruct(t) })
}

func testSimplestStruct(t *testing.T) {
Expand All @@ -42,11 +38,9 @@ func testSimplestStruct(t *testing.T) {
so(s, eq, `{"s":"Hello","i":2023}`)
}

type nestedStruct struct {
ID string `json:"id"`

SubWithEmpty *nestedStruct `json:"sub_with_empty"`
Sub *nestedStruct `json:"sub,omitempty"`
type simplestStruct struct {
S string `json:"s"`
I int `json:"i"`
}

func testNestedStruct(t *testing.T) {
Expand All @@ -67,3 +61,40 @@ func testNestedStruct(t *testing.T) {
t.Log(s)
so(s, eq, `{"id":"parent","sub_with_empty":null,"sub":{"id":"child","sub_with_empty":null}}`)
}

type nestedStruct struct {
ID string `json:"id"`

SubWithEmpty *nestedStruct `json:"sub_with_empty"`
Sub *nestedStruct `json:"sub,omitempty"`
}

func testStructNestingOtherStruct(t *testing.T) {
st := nestingOtherStruct{
ID: "nesting",
}
st.Simple.S = "nested"
st.Simple.I = 1
st.Anonymous.Name = "anonymous name"

e, err := ParseExporter(st)
so(err, eq, nil)

t.Log("Got:", e)

v := e.Export(st)
s := v.MustMarshalString(jsonvalue.OptSetSequence())
t.Log(s)

so(s, eq, `{"id":"nesting","simplest":{"s":"nested","i":1},"anonymous":{"name":"anonymous name"}}`)
}

type nestingOtherStruct struct {
ID string `json:"id"`

Simple simplestStruct `json:"simplest"`

Anonymous struct {
Name string `json:"name"`
} `json:"anonymous"`
}

0 comments on commit c825784

Please sign in to comment.