Skip to content

Commit

Permalink
Fix/correct type conv logic (#40)
Browse files Browse the repository at this point in the history
* fix: avoid using same reference

* test: add more test case

* feat: update comment

* feat: update comment

* refactor: remove returning error

---------

Co-authored-by: Eyo Chen <[email protected]>
  • Loading branch information
eyo-chen and Eyo Chen authored Sep 29, 2024
1 parent db0476c commit 0521de6
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 13 deletions.
49 changes: 43 additions & 6 deletions gofacto.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,20 @@ func (b *builderList[T]) SetZero(i int, fields ...string) *builderList[T] {
return b
}

// WihtOne set one association to the factory value.
// Must pass a pointer to the association value.
// WithOne sets one or more single-value associations for the factory.
//
// This function supports setting associations for both single-level and multi-level relationships.
// Each argument must be a pointer to a struct representing the associated entity.
//
// Examples:
//
// 1. Single-level association (e.g., Transaction has a User):
// transactionFactory.WithOne(&User{})
//
// 2. Multi-level association (e.g., Transaction -> Category -> User):
// transactionFactory.WithOne(&Category{}, &User{})
//
// Note: All arguments must be pointers to structs. Non-pointer or non-struct arguments will result in an error.
func (b *builder[T]) WithOne(vals ...interface{}) *builder[T] {
if b.err != nil {
return b
Expand All @@ -438,8 +450,20 @@ func (b *builder[T]) WithOne(vals ...interface{}) *builder[T] {
return b
}

// WihtOne set one association to the factory value.
// Must pass a pointer to the association value.
// WithOne sets one or more single-value associations for the factory.
//
// This function supports setting associations for both single-level and multi-level relationships.
// Each argument must be a pointer to a struct representing the associated entity.
//
// Examples:
//
// 1. Single-level association (e.g., Transaction has a User):
// transactionFactory.WithOne(&User{})
//
// 2. Multi-level association (e.g., Transaction -> Category -> User):
// transactionFactory.WithOne(&Category{}, &User{})
//
// Note: All arguments must be pointers to structs. Non-pointer or non-struct arguments will result in an error.
func (b *builderList[T]) WithOne(vals ...interface{}) *builderList[T] {
if b.err != nil {
return b
Expand All @@ -457,8 +481,21 @@ func (b *builderList[T]) WithOne(vals ...interface{}) *builderList[T] {
return b
}

// WithMany set many associations to the factory value.
// Must pass a pointer to the association value.
// WithMany sets multiple associations of the same type for each item in the factory list.
//
// The input must be a slice of interface{}, where each element is a pointer to a struct of the same type.
//
// Example:
//
// 1. Single-level association (e.g., Transactions has multiple Users):
// transactionFactory.WithMany([]interface{}{&User{}, &User{}})
//
// 2. Multi-level association (e.g., Transaction -> Category -> User):
// transactionFactory.WithMany([]interface{}{&Category{}, &Category{}}).WithMany([]interface{}{&User{}, &User{}})
//
// Note:
// - All elements in the input slice must be pointers to structs of the same type.
// - Non-pointer, non-struct, or mixed-type arguments will result in an error.
func (b *builderList[T]) WithMany(vals []interface{}) *builderList[T] {
if b.err != nil {
return b
Expand Down
34 changes: 28 additions & 6 deletions typeconv/typeconv.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
package typeconv

// ToAnysWithOW converts the given number of values to a slice of pointers of given type with the given one overwrite.
import (
"reflect"
)

// ToAnysWithOW generates the given number of values to a slice of pointers of given type with the given one overwrite.
func ToAnysWithOW[T any](i int, ow *T) []interface{} {
res := make([]interface{}, i)
for k := 0; k < i; k++ {
var v T
if ow != nil {
res[k] = ow
copyValues(&v, *ow)
res[k] = &v
continue
}

var v T
res[k] = &v
}

return res
}

// ToAnysWithOWs converts the given number of values to a slice of pointers to the given type with the given multiple overwrites.
// ToAnysWithOWs generates the given number of values to a slice of pointers to the given type with the given multiple overwrites.
func ToAnysWithOWs[T any](i int, ows ...*T) []interface{} {
res := make([]interface{}, i)
for k := 0; k < i; k++ {
var v T
res[k] = &v

if ows != nil && k < len(ows) {
res[k] = ows[k]
copyValues(&v, *ows[k])
res[k] = &v
continue
}

res[k] = &v
}

return res
Expand All @@ -50,3 +58,17 @@ func ToPointerT[T any](vals []interface{}) []*T {

return res
}

func copyValues[T any](dest *T, src T) {
destValue := reflect.ValueOf(dest).Elem()
srcValue := reflect.ValueOf(src)

for i := 0; i < destValue.NumField(); i++ {
destField := destValue.Field(i)
srcField := srcValue.FieldByName(destValue.Type().Field(i).Name)

if srcField.IsValid() && destField.Type() == srcField.Type() && !srcField.IsZero() {
destField.Set(srcField)
}
}
}
16 changes: 15 additions & 1 deletion typeconv/typeconv_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package typeconv

import "testing"
import (
"testing"
)

type Person struct {
ID int
Expand Down Expand Up @@ -64,6 +66,12 @@ func TestToAnysWithOW(t *testing.T) {
if *v.(*Person) != *test.want[k].(*Person) {
t.Errorf("expected %v, but got %v", *test.want[k].(*Person), *v.(*Person))
}

if test.i > 0 && k > 0 {
if v == res[0] {
t.Errorf("expected different memory addresses, but got the same for index %d", k)
}
}
}
})
}
Expand Down Expand Up @@ -138,6 +146,12 @@ func TestToAnysWithOWs(t *testing.T) {
if *v.(*Person) != *test.want[k].(*Person) {
t.Errorf("expected %v, but got %v", *test.want[k].(*Person), *v.(*Person))
}

if test.i > 0 && k > 0 {
if v == res[0] {
t.Errorf("expected different memory addresses, but got the same for index %d", k)
}
}
}
})
}
Expand Down

0 comments on commit 0521de6

Please sign in to comment.