diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 096d2ac..706de60 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.21' - name: Build run: go build -v ./... diff --git a/auto.go b/auto.go index 3863839..70e283e 100644 --- a/auto.go +++ b/auto.go @@ -2,6 +2,7 @@ package gomapper import ( "reflect" + "slices" "github.com/insei/fmap/v3" ) @@ -33,6 +34,9 @@ func AutoRoute[TSource, TDest any | []any](opts ...Option) error { if destFld.GetType() != srcFld.GetType() { continue } + if slices.Contains(opt.Excluded, srcFld) { + continue + } if err := setFieldRecursive(srcFld, destFld, source, dest); err != nil { return err } diff --git a/auto_test.go b/auto_test.go index 2dd67d9..dd4b183 100644 --- a/auto_test.go +++ b/auto_test.go @@ -9,6 +9,7 @@ import ( ) type AutoMappingStructSource struct { + Age int Name string Time time.Time UUID uuid.UUID @@ -18,10 +19,11 @@ type AutoMappingStructSource struct { } type AutoMappingStructDest struct { + UUID uuid.UUID + Age int Name string SecondName string Time time.Time - UUID uuid.UUID PtrTime *time.Time PtrUUID *uuid.UUID NestedStruct NestedStructDest @@ -70,15 +72,24 @@ func TestAutoRoute(t *testing.T) { dest.PtrTime = &timeNow } dest.Time = timeNow - })) + }), + WithFieldSkip(func(dest *AutoMappingStructSource) any { + return &dest.UUID + }), + WithFieldSkip(func(dest *AutoMappingStructSource) any { + return &dest.Age + }), + ) t.Run("Auto route with options", func(t *testing.T) { - source := &AutoMappingStructSource{Name: "Test1"} + source := &AutoMappingStructSource{UUID: uuid.New(), Name: "Test1", Age: 25} dest, err := MapTo[AutoMappingStructDest](source) assert.NoError(t, err) assert.Equal(t, source.Name, dest.Name) assert.Equal(t, "Test2", dest.SecondName) assert.Equal(t, timeNow, *dest.PtrTime) assert.Equal(t, timeNow, dest.Time) + assert.Equal(t, 0, dest.Age) + assert.Equal(t, uuid.Nil, dest.UUID) }) t.Run("Auto mapping struct fields", func(t *testing.T) { source := &AutoMappingStructSource{ diff --git a/go.mod b/go.mod index 4463cea..077019d 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/insei/gomapper -go 1.18 +go 1.21 require ( github.com/google/uuid v1.6.0 - github.com/insei/fmap/v3 v3.0.0 + github.com/insei/fmap/v3 v3.1.2 github.com/stretchr/testify v1.9.0 ) diff --git a/go.sum b/go.sum index 3eb32d5..f44f3c7 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/insei/fmap/v3 v3.0.0 h1:BpRsFgQ2nt5tl/8tzp6y+MWIAJhqCTeKBHC56L/Um30= -github.com/insei/fmap/v3 v3.0.0/go.mod h1:Kk0gs7nKb4E/JycKJFnrsX5hlyBBe0yetGKFCJG0vzk= +github.com/insei/fmap/v3 v3.1.2 h1:ZBr+WiZpIxFNeMo2X4QOST4AFl0sGAkG+EO08Ved3bY= +github.com/insei/fmap/v3 v3.1.2/go.mod h1:Kk0gs7nKb4E/JycKJFnrsX5hlyBBe0yetGKFCJG0vzk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/options.go b/options.go index c3a8da3..a36734c 100644 --- a/options.go +++ b/options.go @@ -1,11 +1,20 @@ package gomapper +import ( + "github.com/insei/fmap/v3" +) + type withFuncOption[TSource, TDest any] struct { fn func(TSource, *TDest) } +type withFieldSkip[TDest any] struct { + field fmap.Field +} + type options struct { - Fns []any + Fns []any + Excluded []fmap.Field } type Option interface { @@ -16,6 +25,31 @@ func (a withFuncOption[TSource, TDest]) apply(opts *options) { opts.Fns = append(opts.Fns, a.fn) } +func (a withFieldSkip[TDest]) apply(opts *options) { + if opts.Excluded == nil { + opts.Excluded = make([]fmap.Field, 0) + } + opts.Excluded = append(opts.Excluded, a.field) +} + func WithFunc[TSource, TDest any](fn func(TSource, *TDest)) Option { return &withFuncOption[TSource, TDest]{fn: fn} } + +func WithFieldSkip[TSource any](fn func(*TSource) any) Option { + source := new(TSource) + + storage, err := fmap.GetFrom(source) + if err != nil { + panic(err) + } + + fieldPtr := fn(source) + + field, err := storage.GetFieldByPtr(source, fieldPtr) + if err != nil { + panic(err) + } + + return &withFieldSkip[TSource]{field: field} +}