diff --git a/cli.go b/cli.go index 424b0a7..2ae304b 100644 --- a/cli.go +++ b/cli.go @@ -72,21 +72,23 @@ func NewCliApp() *cli.App { if err := os.WriteFile(outputFilename, buf.Bytes(), 0644); err != nil { return fmt.Errorf("writing output file: %w", err) } - } else { - mappings, err := internal.DiscoverStructMappings(mappingTypePackage) + + return nil + } + + mappings, err := internal.DiscoverStructMappings(mappingTypePackage) + if err != nil { + return fmt.Errorf("discovering struct mappings: %w", err) + } + + for _, m := range mappings { + var buf bytes.Buffer + outputFilename, err := internal.Generate(m, goPackage, goFile, &buf) if err != nil { - return fmt.Errorf("discovering struct mappings: %w", err) + return fmt.Errorf("generating code: %w", err) } - - for _, m := range mappings { - var buf bytes.Buffer - outputFilename, err := internal.Generate(m, goPackage, goFile, &buf) - if err != nil { - return fmt.Errorf("generating code: %w", err) - } - if err := os.WriteFile(outputFilename, buf.Bytes(), 0644); err != nil { - return fmt.Errorf("writing output file: %w", err) - } + if err := os.WriteFile(outputFilename, buf.Bytes(), 0644); err != nil { + return fmt.Errorf("writing output file: %w", err) } } diff --git a/example/pgx/model/mappings.go b/example/pgx/model/mappings.go new file mode 100644 index 0000000..950d322 --- /dev/null +++ b/example/pgx/model/mappings.go @@ -0,0 +1,3 @@ +package model + +//go:generate go run github.com/networkteam/construct/v2/cmd/construct github.com/networkteam/construct/v2/example/pgx/model diff --git a/go.work.sum b/go.work.sum index fecb356..b808f2b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,4 +1,5 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= @@ -6,14 +7,22 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14 h1:YI1gOOdmMk3xodBao7fehcvoZsEeOyy/cfhlpCSPgM4= +github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms= github.com/dave/brenda v1.1.0 h1:Sl1LlwXnbw7xMhq3y2x11McFu43AjDcwkllxxgZ3EZw= +github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM= github.com/dave/courtney v0.3.0 h1:8aR1os2ImdIQf3Zj4oro+lD/L4Srb5VwGefqZ/jzz7U= +github.com/dave/courtney v0.3.0/go.mod h1:BAv3hA06AYfNUjfjQr+5gc6vxeBVOupLqrColj+QSD8= github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e h1:l99YKCdrK4Lvb/zTupt0GMPfNbncAGf8Cv/t1sYLOg0= +github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ= github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e h1:xURkGi4RydhyaYR6PzcyHTueQudxY4LgxN1oYEPJHa0= +github.com/dave/kerr v0.0.0-20170318121727-bc25dd6abe8e/go.mod h1:qZqlPyPvfsDJt+3wHJ1EvSXDuVjFTK0j2p/ca+gtsb8= github.com/dave/patsy v0.0.0-20210517141501-957256f50cba h1:1o36L4EKbZzazMk8iGC4kXpVnZ6TPxR2mZ9qVKjNNAs= +github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc= github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= +github.com/dave/rebecca v0.9.1/go.mod h1:N6XYdMD/OKw3lkF3ywh8Z6wPGuwNFDNtWYEMFWEmXBA= github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -54,3 +63,4 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/fixtures/fixture_mytype_gen.go b/internal/fixtures/fixture_mytype_gen.go new file mode 100644 index 0000000..8e86e87 --- /dev/null +++ b/internal/fixtures/fixture_mytype_gen.go @@ -0,0 +1,99 @@ +// Code generated by construct, DO NOT EDIT. +package fixtures + +import ( + "encoding/json" + uuid "github.com/gofrs/uuid" + qrb "github.com/networkteam/qrb" + builder "github.com/networkteam/qrb/builder" + fn "github.com/networkteam/qrb/fn" + "time" +) + +var myType = struct { + builder.Identer + ID builder.IdentExp + Foo builder.IdentExp + Bar builder.IdentExp + Baz builder.IdentExp + LastTime builder.IdentExp + LastUpdate builder.IdentExp + Donuts builder.IdentExp +}{ + Bar: qrb.N("my_type.the_bar"), + Baz: qrb.N("my_type.baz"), + Donuts: qrb.N("my_type.donuts"), + Foo: qrb.N("my_type.foo"), + ID: qrb.N("my_type.id"), + Identer: qrb.N("my_type"), + LastTime: qrb.N("my_type.last_time"), + LastUpdate: qrb.N("my_type.updated_at"), +} + +var myTargetTypeSortFields = map[string]builder.IdentExp{ + "foo": myType.Foo, + "lasttime": myType.LastTime, + "lastupdate": myType.LastUpdate, +} + +type MyTargetTypeChangeSet struct { + ID *uuid.UUID + Foo *string + Bar []byte + Baz *MyEmbeddedType + LastTime **time.Time + LastUpdate *time.Time + Donuts []Donut +} + +func (c MyTargetTypeChangeSet) toMap() map[string]interface{} { + m := make(map[string]interface{}) + if c.ID != nil { + m["id"] = *c.ID + } + if c.Foo != nil { + m["foo"] = *c.Foo + } + if c.Bar != nil { + m["the_bar"] = c.Bar + } + if c.Baz != nil { + data, _ := json.Marshal(c.Baz) + m["baz"] = data + } + if c.LastTime != nil { + m["last_time"] = *c.LastTime + } + if c.LastUpdate != nil { + m["updated_at"] = *c.LastUpdate + } + if c.Donuts != nil { + data, _ := json.Marshal(c.Donuts) + m["donuts"] = data + } + return m +} + +func MyTargetTypeToChangeSet(r MyType) (c MyTargetTypeChangeSet) { + if r.ID != uuid.Nil { + c.ID = &r.ID + } + c.Foo = &r.Foo + c.Bar = r.Bar + c.Baz = &r.Baz + c.LastTime = &r.LastTime + if !r.LastUpdate.IsZero() { + c.LastUpdate = &r.LastUpdate + } + c.Donuts = r.Donuts + return +} + +var myTargetTypeDefaultJson = fn.JsonBuildObject(). + Prop("ID", myType.ID). + Prop("Foo", myType.Foo). + Prop("Bar", qrb.Func("ENCODE", myType.Bar, qrb.String("BASE64"))). + Prop("Baz", myType.Baz). + Prop("LastTime", myType.LastTime). + Prop("LastUpdate", myType.LastUpdate). + Prop("Donuts", myType.Donuts) diff --git a/internal/generate.go b/internal/generate.go index c037d6d..655633f 100644 --- a/internal/generate.go +++ b/internal/generate.go @@ -12,6 +12,8 @@ import ( // Generate Go code for the struct mapping func Generate(m *StructMapping, goPackage string, goFile string, w io.Writer) (outputFilename string, err error) { + pkgName := m.MappingTypePackage[strings.LastIndex(m.MappingTypePackage, "/")+1:] + f := NewFile(goPackage) f.PackageComment("Code generated by construct, DO NOT EDIT.") @@ -22,7 +24,7 @@ func Generate(m *StructMapping, goPackage string, goFile string, w io.Writer) (o // ChangeSet struct - changeSetName, err := generateChangeSetStruct(f, m) + changeSetName, err := generateChangeSetStruct(f, m, goPackage) if err != nil { return "", fmt.Errorf("generating ChangeSet struct: %w", err) } @@ -100,8 +102,12 @@ func Generate(m *StructMapping, goPackage string, goFile string, w io.Writer) (o toChangeSetBlock = append(toChangeSetBlock, Return()) + mtp := m.MappingTypePackage + if goPackage == pkgName { + mtp = "" + } f.Func().Id(firstToUpper(m.TargetName) + "ToChangeSet").Params( - Id("r").Qual(m.MappingTypePackage, m.MappingTypeName), + Id("r").Qual(mtp, m.MappingTypeName), ).Params(Id("c").Id(changeSetName)).Block( toChangeSetBlock..., ).Line() @@ -163,7 +169,7 @@ func generateDefaultSelectJsonObject(f *File, m *StructMapping) { f.Var().Id(varName).Op("=").Add(code).Line() } -func generateChangeSetStruct(f *File, m *StructMapping) (changeSetName string, err error) { +func generateChangeSetStruct(f *File, m *StructMapping, pkgDest string) (changeSetName string, err error) { var structFields []Code for _, fm := range m.FieldMappings { @@ -174,8 +180,13 @@ func generateChangeSetStruct(f *File, m *StructMapping) (changeSetName string, e code.Op("*").Id(v.String()) case *types.Named: typeName := v.Obj() + pkgPath := typeName.Pkg().Path() + pkgName := pkgPath[strings.LastIndex(pkgPath, "/")+1:] + if pkgDest == pkgName { + pkgPath = "" + } code.Op("*").Qual( - typeName.Pkg().Path(), + pkgPath, typeName.Name(), ) case *types.Pointer: @@ -186,8 +197,13 @@ func generateChangeSetStruct(f *File, m *StructMapping) (changeSetName string, e code.Op("*").Id(v.String()) case *types.Named: typeName := v.Obj() + pkgPath := typeName.Pkg().Path() + pkgName := pkgPath[strings.LastIndex(pkgPath, "/")+1:] + if pkgDest == pkgName { + pkgPath = "" + } code.Op("*").Qual( - typeName.Pkg().Path(), + pkgPath, typeName.Name(), ) case *types.Slice: @@ -203,8 +219,13 @@ func generateChangeSetStruct(f *File, m *StructMapping) (changeSetName string, e code.Id(v.String()) case *types.Named: typeName := v.Obj() + pkgPath := typeName.Pkg().Path() + pkgName := pkgPath[strings.LastIndex(pkgPath, "/")+1:] + if pkgDest == pkgName { + pkgPath = "" + } code.Qual( - typeName.Pkg().Path(), + pkgPath, typeName.Name(), ) default: diff --git a/internal/generate_test.go b/internal/generate_test.go index 2dbe46a..5b3ea92 100644 --- a/internal/generate_test.go +++ b/internal/generate_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/networkteam/construct/v2/internal" ) @@ -15,21 +16,29 @@ func TestGenerate(t *testing.T) { var buf bytes.Buffer outputFilename, err := internal.Generate(m, "repository", "mappings.go", &buf) - if err != nil { - t.Fatalf("error generating code: %v", err) - } + require.NoErrorf(t, err, "error generating code: %v") const expectedOutputFilename = "mappings_mytype_gen.go" - if outputFilename != expectedOutputFilename { - t.Errorf("expected output filename to be %s, but got %s", expectedOutputFilename, outputFilename) - } + require.Equal(t, expectedOutputFilename, outputFilename, "expected output filename to be %s, but got %s", expectedOutputFilename, outputFilename) fixtureOut, err := os.ReadFile("./fixtures/repository/" + expectedOutputFilename) - if err != nil { - t.Fatalf("error reading fixture file: %v", err) - } + require.NoError(t, err, "error reading fixture file: %v") - if buf.String() != string(fixtureOut) { - assert.Equal(t, string(fixtureOut), buf.String()) - } + assert.Equal(t, string(fixtureOut), buf.String()) +} + +func TestGenerateSamePackage(t *testing.T) { + m := myTypeStructMapping() + + var buf bytes.Buffer + outputFilename, err := internal.Generate(m, "fixtures", "fixture.go", &buf) + require.NoErrorf(t, err, "error generating code: %v") + + const expectedOutputFilename = "fixture_mytype_gen.go" + require.Equal(t, expectedOutputFilename, outputFilename, "expected output filename to be %s, but got %s", expectedOutputFilename, outputFilename) + + fixtureOut, err := os.ReadFile("./fixtures/" + expectedOutputFilename) + require.NoError(t, err, "error reading fixture file: %v") + + assert.Equal(t, string(fixtureOut), buf.String()) }