-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
825 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
|
||
} |
Oops, something went wrong.