Skip to content

Commit

Permalink
feat: 添加元组封装
Browse files Browse the repository at this point in the history
  • Loading branch information
TBXark committed Nov 14, 2024
1 parent 9c02c5a commit cd0c0c2
Show file tree
Hide file tree
Showing 5 changed files with 825 additions and 0 deletions.
102 changes: 102 additions & 0 deletions pkg/utils/tuple/gen/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package gen

import (
"fmt"
"io"
"text/template"
)

type Field struct {
Name string
Type string
}

type Desc struct {
Num int
Fields []Field
}

const tupleTemplate = `// Package tuple Code generated by go generate; DO NOT EDIT.
package tuple
import (
"encoding/json"
"fmt"
)
{{range .}}
// Of{{.Num}} represents a tuple of {{.Num}} elements
type Of{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}} any{{end}}] struct {
{{range .Fields}}{{.Name}} {{.Type}}
{{end}}
}
// New{{.Num}} creates a new tuple of {{.Num}} elements
func New{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}} any{{end}}]({{range $i, $f := .Fields}}{{if $i}}, {{end}}{{lower .Name}} {{.Type}}{{end}}) Of{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}}{{end}}] {
return Of{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}}{{end}}]{
{{range .Fields}}{{.Name}}: {{lower .Name}},
{{end}}
}
}
// MarshalJSON implements json.Marshaler interface
func (t *Of{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}}{{end}}]) MarshalJSON() ([]byte, error) {
return json.Marshal([]interface{}{ {{range $i, $f := .Fields}}{{if $i}}, {{end}}t.{{.Name}}{{end}} })
}
// UnmarshalJSON implements json.Unmarshaler interface
func (t *Of{{.Num}}[{{range $i, $f := .Fields}}{{if $i}}, {{end}}{{.Type}}{{end}}]) UnmarshalJSON(data []byte) error {
var raw []json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return fmt.Errorf("failed to unmarshal array: %w", err)
}
if len(raw) < {{.Num}} {
return fmt.Errorf("expected array of length {{.Num}}, got %d", len(raw))
}
{{range $i, $f := .Fields}}
if err := json.Unmarshal(raw[{{$i}}], &t.{{.Name}}); err != nil {
return fmt.Errorf("failed to unmarshal {{lower .Name}} element: %w", err)
}
{{end}}
return nil
}
{{end}}
`

func Gen(writer io.Writer) {
fieldNames := []string{
"First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth", "Tenth",
}
typesNames := []string{
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
}
var list []Desc
for i := 2; i <= 10; i++ {
row := Desc{Num: i}
for j := 0; j < i; j++ {
row.Fields = append(row.Fields, Field{
Name: fieldNames[j],
Type: typesNames[j],
})
}
list = append(list, row)
}

tmpl, err := template.New("tuple").Funcs(template.FuncMap{
"lower": func(s string) string {
return string(s[0]+32) + s[1:]
},
}).Parse(tupleTemplate)
if err != nil {
fmt.Println("Error parsing template:", err)
return
}

err = tmpl.Execute(writer, list)
if err != nil {
fmt.Println("Error executing template:", err)
}
}
43 changes: 43 additions & 0 deletions pkg/utils/tuple/setter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package tuple

import (
"fmt"
"reflect"
)

func SetToStruct(tuple interface{}, dst interface{}, offset int) error {
tupleValue := reflect.ValueOf(tuple)
if tupleValue.Kind() == reflect.Ptr {
tupleValue = tupleValue.Elem()
}
if tupleValue.Kind() != reflect.Struct {
return fmt.Errorf("tuple must be a struct, got %v", tupleValue.Kind())
}
dstValue := reflect.ValueOf(dst)
if dstValue.Kind() != reflect.Ptr {
return fmt.Errorf("destination must be a pointer, got %v", dstValue.Kind())
}
dstValue = dstValue.Elem()
if dstValue.Kind() != reflect.Struct {
return fmt.Errorf("destination must be a pointer to struct, got pointer to %v", dstValue.Kind())
}
tupleFields := tupleValue.NumField()
dstFields := dstValue.NumField()
for i := 0; i < tupleFields; i++ {
if i+offset < 0 || i+offset >= dstFields {
continue
}
tupleField := tupleValue.Field(i)
dstField := dstValue.Field(i + offset)
if !dstField.CanSet() {
return fmt.Errorf("field %d in destination struct is not settable", i)
}
if !tupleField.Type().AssignableTo(dstField.Type()) {
return fmt.Errorf("field %d type mismatch: cannot assign %v to %v",
i, tupleField.Type(), dstField.Type())
}
dstField.Set(tupleField)
}

return nil
}
38 changes: 38 additions & 0 deletions pkg/utils/tuple/setter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package tuple

import "testing"

func TestSetToStruct(t *testing.T) {
var test struct {
Key string
Value int
}
tuple := New2("hello", 42)
err := SetToStruct(&tuple, &test, 0)
if err != nil {
t.Fatal(err)
}
if test.Key != tuple.First {
t.Fatalf("expected %q, got %q", tuple.First, test.Key)
}
if test.Value != tuple.Second {
t.Fatalf("expected %d, got %d", tuple.Second, test.Value)
}

var test2 struct {
hidden string
Key string
Value int
}
err = SetToStruct(&tuple, &test2, 1)
if err != nil {
t.Fatal(err)
}
if test2.Key != tuple.First {
t.Fatalf("expected %q, got %q", tuple.First, test2.Key)
}
if test2.Value != tuple.Second {
t.Fatalf("expected %d, got %d", tuple.Second, test2.Value)
}

}
Loading

0 comments on commit cd0c0c2

Please sign in to comment.