Scan directory to find source code in multiple files in the same package (#30)
This commit is contained in:
parent
0a1d5c8545
commit
737c1a4044
15 changed files with 444 additions and 177 deletions
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
|
@ -10,15 +10,11 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.17
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install dependencies
|
||||
run: go get -u golang.org/x/lint/golint
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
@ -26,10 +22,8 @@ jobs:
|
|||
- name: Test
|
||||
run: go test -v ./... -covermode=count -coverprofile=cover.out
|
||||
|
||||
- name: Vet & Lint
|
||||
run: |
|
||||
go vet ./...
|
||||
golint -set_exit_status ./...
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
|
|
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
|
@ -10,9 +10,13 @@ jobs:
|
|||
golangci-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.40.1
|
||||
version: v1.50.0
|
||||
|
|
21
go.mod
21
go.mod
|
@ -1,26 +1,25 @@
|
|||
module github.com/sunboyy/repogen
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
go.mongodb.org/mongo-driver v1.9.1
|
||||
golang.org/x/tools v0.1.10
|
||||
go.mongodb.org/mongo-driver v1.10.3
|
||||
golang.org/x/tools v0.1.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
|
61
go.sum
61
go.sum
|
@ -3,8 +3,6 @@ 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/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
|
@ -14,6 +12,7 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
|
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -26,56 +25,38 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
|||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
|
||||
go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA=
|
||||
go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
28
internal/code/errors.go
Normal file
28
internal/code/errors.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package code
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAmbiguousPackageName = errors.New("code: ambiguous package name")
|
||||
)
|
||||
|
||||
type DuplicateStructError string
|
||||
|
||||
func (err DuplicateStructError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"code: duplicate implementation of struct '%s'",
|
||||
string(err),
|
||||
)
|
||||
}
|
||||
|
||||
type DuplicateInterfaceError string
|
||||
|
||||
func (err DuplicateInterfaceError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"code: duplicate implementation of interface '%s'",
|
||||
string(err),
|
||||
)
|
||||
}
|
36
internal/code/errors_test.go
Normal file
36
internal/code/errors_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package code_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sunboyy/repogen/internal/code"
|
||||
)
|
||||
|
||||
type ErrorTestCase struct {
|
||||
Name string
|
||||
Error error
|
||||
ExpectedString string
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
testTable := []ErrorTestCase{
|
||||
{
|
||||
Name: "DuplicateStructError",
|
||||
Error: code.DuplicateStructError("User"),
|
||||
ExpectedString: "code: duplicate implementation of struct 'User'",
|
||||
},
|
||||
{
|
||||
Name: "DuplicateInterfaceError",
|
||||
Error: code.DuplicateInterfaceError("UserRepository"),
|
||||
ExpectedString: "code: duplicate implementation of interface 'UserRepository'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testTable {
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
if testCase.Error.Error() != testCase.ExpectedString {
|
||||
t.Errorf("Expected = %+v\nReceived = %+v", testCase.ExpectedString, testCase.Error.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -64,8 +64,8 @@ type UserModel struct {
|
|||
}`,
|
||||
ExpectedOutput: code.File{
|
||||
PackageName: "user",
|
||||
Structs: code.Structs{
|
||||
code.Struct{
|
||||
Structs: []code.Struct{
|
||||
{
|
||||
Name: "UserModel",
|
||||
Fields: code.StructFields{
|
||||
code.StructField{
|
||||
|
@ -108,8 +108,8 @@ type UserRepository interface {
|
|||
}`,
|
||||
ExpectedOutput: code.File{
|
||||
PackageName: "user",
|
||||
Interfaces: code.Interfaces{
|
||||
code.InterfaceType{
|
||||
Interfaces: []code.InterfaceType{
|
||||
{
|
||||
Name: "UserRepository",
|
||||
Methods: []code.Method{
|
||||
{
|
||||
|
@ -229,8 +229,8 @@ type UserRepository interface {
|
|||
{Path: "context"},
|
||||
{Path: "go.mongodb.org/mongo-driver/bson/primitive"},
|
||||
},
|
||||
Structs: code.Structs{
|
||||
code.Struct{
|
||||
Structs: []code.Struct{
|
||||
{
|
||||
Name: "UserModel",
|
||||
Fields: code.StructFields{
|
||||
code.StructField{
|
||||
|
@ -252,8 +252,8 @@ type UserRepository interface {
|
|||
},
|
||||
},
|
||||
},
|
||||
Interfaces: code.Interfaces{
|
||||
code.InterfaceType{
|
||||
Interfaces: []code.InterfaceType{
|
||||
{
|
||||
Name: "UserRepository",
|
||||
Methods: []code.Method{
|
||||
{
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
type File struct {
|
||||
PackageName string
|
||||
Imports []Import
|
||||
Structs Structs
|
||||
Interfaces Interfaces
|
||||
Structs []Struct
|
||||
Interfaces []InterfaceType
|
||||
}
|
||||
|
||||
// Import is a model for package imports
|
||||
|
@ -18,20 +18,6 @@ type Import struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
// Structs is a group of Struct model
|
||||
type Structs []Struct
|
||||
|
||||
// ByName return struct with matching name. Another return value shows whether there is a struct
|
||||
// with that name exists.
|
||||
func (strs Structs) ByName(name string) (Struct, bool) {
|
||||
for _, str := range strs {
|
||||
if str.Name == name {
|
||||
return str, true
|
||||
}
|
||||
}
|
||||
return Struct{}, false
|
||||
}
|
||||
|
||||
// Struct is a definition of the struct
|
||||
type Struct struct {
|
||||
Name string
|
||||
|
@ -63,20 +49,6 @@ type StructField struct {
|
|||
Tags map[string][]string
|
||||
}
|
||||
|
||||
// Interfaces is a group of Interface model
|
||||
type Interfaces []InterfaceType
|
||||
|
||||
// ByName return interface by name Another return value shows whether there is an interface
|
||||
// with that name exists.
|
||||
func (intfs Interfaces) ByName(name string) (InterfaceType, bool) {
|
||||
for _, intf := range intfs {
|
||||
if intf.Name == name {
|
||||
return intf, true
|
||||
}
|
||||
}
|
||||
return InterfaceType{}, false
|
||||
}
|
||||
|
||||
// InterfaceType is a definition of the interface
|
||||
type InterfaceType struct {
|
||||
Name string
|
||||
|
|
|
@ -7,36 +7,6 @@ import (
|
|||
"github.com/sunboyy/repogen/internal/code"
|
||||
)
|
||||
|
||||
func TestStructsByName(t *testing.T) {
|
||||
userStruct := code.Struct{
|
||||
Name: "UserModel",
|
||||
Fields: code.StructFields{
|
||||
code.StructField{Name: "ID", Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
|
||||
code.StructField{Name: "Username", Type: code.SimpleType("string")},
|
||||
},
|
||||
}
|
||||
structs := code.Structs{userStruct}
|
||||
|
||||
t.Run("struct found", func(t *testing.T) {
|
||||
structModel, ok := structs.ByName("UserModel")
|
||||
|
||||
if !ok {
|
||||
t.Fail()
|
||||
}
|
||||
if !reflect.DeepEqual(structModel, userStruct) {
|
||||
t.Errorf("Expected = %+v\nReceived = %+v", userStruct, structModel)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("struct not found", func(t *testing.T) {
|
||||
_, ok := structs.ByName("ProductModel")
|
||||
|
||||
if ok {
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructFieldsByName(t *testing.T) {
|
||||
idField := code.StructField{Name: "ID", Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}}
|
||||
usernameField := code.StructField{Name: "Username", Type: code.SimpleType("string")}
|
||||
|
@ -62,30 +32,6 @@ func TestStructFieldsByName(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestInterfacesByName(t *testing.T) {
|
||||
userRepoIntf := code.InterfaceType{Name: "UserRepository"}
|
||||
interfaces := code.Interfaces{userRepoIntf}
|
||||
|
||||
t.Run("struct field found", func(t *testing.T) {
|
||||
intf, ok := interfaces.ByName("UserRepository")
|
||||
|
||||
if !ok {
|
||||
t.Fail()
|
||||
}
|
||||
if !reflect.DeepEqual(intf, userRepoIntf) {
|
||||
t.Errorf("Expected = %+v\nReceived = %+v", userRepoIntf, intf)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("struct field not found", func(t *testing.T) {
|
||||
_, ok := interfaces.ByName("Password")
|
||||
|
||||
if ok {
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type TypeCodeTestCase struct {
|
||||
Name string
|
||||
Type code.Type
|
||||
|
|
66
internal/code/package.go
Normal file
66
internal/code/package.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package code
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParsePackage extracts package name, struct and interface implementations from
|
||||
// map[string]*ast.Package. Test files will be ignored.
|
||||
func ParsePackage(pkgs map[string]*ast.Package) (Package, error) {
|
||||
pkg := NewPackage()
|
||||
for _, astPkg := range pkgs {
|
||||
for fileName, file := range astPkg.Files {
|
||||
if strings.HasSuffix(fileName, "_test.go") {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := pkg.addFile(ExtractComponents(file)); err != nil {
|
||||
return Package{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
// Package stores package name, struct and interface implementations as a result
|
||||
// from ParsePackage
|
||||
type Package struct {
|
||||
Name string
|
||||
Structs map[string]Struct
|
||||
Interfaces map[string]InterfaceType
|
||||
}
|
||||
|
||||
// NewPackage is a constructor function for Package.
|
||||
func NewPackage() Package {
|
||||
return Package{
|
||||
Structs: map[string]Struct{},
|
||||
Interfaces: map[string]InterfaceType{},
|
||||
}
|
||||
}
|
||||
|
||||
// addFile alters the Package by adding struct and interface implementations in
|
||||
// the extracted file. If the package name conflicts, it will return error.
|
||||
func (pkg *Package) addFile(file File) error {
|
||||
if pkg.Name == "" {
|
||||
pkg.Name = file.PackageName
|
||||
} else if pkg.Name != file.PackageName {
|
||||
return ErrAmbiguousPackageName
|
||||
}
|
||||
|
||||
for _, structImpl := range file.Structs {
|
||||
if _, ok := pkg.Structs[structImpl.Name]; ok {
|
||||
return DuplicateStructError(structImpl.Name)
|
||||
}
|
||||
pkg.Structs[structImpl.Name] = structImpl
|
||||
}
|
||||
|
||||
for _, interfaceImpl := range file.Interfaces {
|
||||
if _, ok := pkg.Interfaces[interfaceImpl.Name]; ok {
|
||||
return DuplicateInterfaceError(interfaceImpl.Name)
|
||||
}
|
||||
pkg.Interfaces[interfaceImpl.Name] = interfaceImpl
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
238
internal/code/package_test.go
Normal file
238
internal/code/package_test.go
Normal file
|
@ -0,0 +1,238 @@
|
|||
package code_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"github.com/sunboyy/repogen/internal/code"
|
||||
)
|
||||
|
||||
const goImplFile1Data = `
|
||||
package codepkgsuccess
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type Gender string
|
||||
|
||||
const (
|
||||
GenderMale Gender = "MALE"
|
||||
GenderFemale Gender = "FEMALE"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID primitive.ObjectID ` + "`json:\"id\"`" + `
|
||||
Name string ` + "`json:\"name\"`" + `
|
||||
Gender Gender ` + "`json:\"gender\"`" + `
|
||||
Birthday time.Time ` + "`json:\"birthday\"`" + `
|
||||
}
|
||||
|
||||
func (u User) Age() int {
|
||||
return int(math.Floor(time.Since(u.Birthday).Hours() / 24 / 365))
|
||||
}
|
||||
|
||||
type (
|
||||
Product struct {
|
||||
ID primitive.ObjectID ` + "`json:\"id\"`" + `
|
||||
Name string ` + "`json:\"name\"`" + `
|
||||
Price float64 ` + "`json:\"price\"`" + `
|
||||
}
|
||||
|
||||
Order struct {
|
||||
ID primitive.ObjectID ` + "`json:\"id\"`" + `
|
||||
ItemIDs map[primitive.ObjectID]int ` + "`json:\"itemIds\"`" + `
|
||||
TotalPrice float64 ` + "`json:\"totalPrice\"`" + `
|
||||
UserID primitive.ObjectID ` + "`json:\"userId\"`" + `
|
||||
CreatedAt time.Time ` + "`json:\"createdAt\"`" + `
|
||||
}
|
||||
)
|
||||
`
|
||||
|
||||
const goImplFile2Data = `
|
||||
package codepkgsuccess
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type OrderService interface {
|
||||
CreateOrder(u User, products map[Product]int) Order
|
||||
}
|
||||
|
||||
type OrderServiceImpl struct{}
|
||||
|
||||
func (s *OrderServiceImpl) CreateOrder(u User, products map[Product]int) Order {
|
||||
itemIDs := map[primitive.ObjectID]int{}
|
||||
var totalPrice float64
|
||||
for product, amount := range products {
|
||||
itemIDs[product.ID] = amount
|
||||
totalPrice += product.Price * float64(amount)
|
||||
}
|
||||
|
||||
return Order{
|
||||
ID: primitive.NewObjectID(),
|
||||
ItemIDs: map[primitive.ObjectID]int{},
|
||||
TotalPrice: totalPrice,
|
||||
UserID: u.ID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const goImplFile3Data = `
|
||||
package success
|
||||
`
|
||||
|
||||
const goImplFile4Data = `
|
||||
package codepkgsuccess
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
`
|
||||
|
||||
const goImplFile5Data = `
|
||||
package codepkgsuccess
|
||||
|
||||
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
type OrderService interface {
|
||||
CancelOrder(orderID primitive.ObjectID) error
|
||||
}
|
||||
`
|
||||
|
||||
const goTestFileData = `
|
||||
package codepkgsuccess
|
||||
|
||||
type TestCase struct {
|
||||
Name string
|
||||
Params []interface{}
|
||||
Expected string
|
||||
Actual string
|
||||
}
|
||||
`
|
||||
|
||||
var (
|
||||
goImplFile1 *ast.File
|
||||
goImplFile2 *ast.File
|
||||
goImplFile3 *ast.File
|
||||
goImplFile4 *ast.File
|
||||
goImplFile5 *ast.File
|
||||
goTestFile *ast.File
|
||||
)
|
||||
|
||||
func init() {
|
||||
fset := token.NewFileSet()
|
||||
goImplFile1, _ = parser.ParseFile(fset, "", goImplFile1Data, parser.ParseComments)
|
||||
goImplFile2, _ = parser.ParseFile(fset, "", goImplFile2Data, parser.ParseComments)
|
||||
goImplFile3, _ = parser.ParseFile(fset, "", goImplFile3Data, parser.ParseComments)
|
||||
goImplFile4, _ = parser.ParseFile(fset, "", goImplFile4Data, parser.ParseComments)
|
||||
goImplFile5, _ = parser.ParseFile(fset, "", goImplFile5Data, parser.ParseComments)
|
||||
goTestFile, _ = parser.ParseFile(fset, "", goTestFileData, parser.ParseComments)
|
||||
}
|
||||
|
||||
func TestParsePackage_Success(t *testing.T) {
|
||||
pkg, err := code.ParsePackage(map[string]*ast.Package{
|
||||
"codepkgsuccess": {
|
||||
Files: map[string]*ast.File{
|
||||
"file1.go": goImplFile1,
|
||||
"file2.go": goImplFile2,
|
||||
"file1_test.go": goTestFile,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pkg.Name != "codepkgsuccess" {
|
||||
t.Errorf("expected package name 'codepkgsuccess', got '%s'", pkg.Name)
|
||||
}
|
||||
if _, ok := pkg.Structs["User"]; !ok {
|
||||
t.Error("struct 'User' not found")
|
||||
}
|
||||
if _, ok := pkg.Structs["Product"]; !ok {
|
||||
t.Error("struct 'Product' not found")
|
||||
}
|
||||
if _, ok := pkg.Structs["Order"]; !ok {
|
||||
t.Error("struct 'Order' not found")
|
||||
}
|
||||
if _, ok := pkg.Structs["OrderServiceImpl"]; !ok {
|
||||
t.Error("struct 'OrderServiceImpl' not found")
|
||||
}
|
||||
if _, ok := pkg.Interfaces["OrderService"]; !ok {
|
||||
t.Error("interface 'OrderService' not found")
|
||||
}
|
||||
if _, ok := pkg.Structs["TestCase"]; ok {
|
||||
t.Error("unexpected struct 'TestCase' in test file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePackage_AmbiguousPackageName(t *testing.T) {
|
||||
_, err := code.ParsePackage(map[string]*ast.Package{
|
||||
"codepkgsuccess": {
|
||||
Files: map[string]*ast.File{
|
||||
"file1.go": goImplFile1,
|
||||
"file2.go": goImplFile2,
|
||||
"file3.go": goImplFile3,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !errors.Is(err, code.ErrAmbiguousPackageName) {
|
||||
t.Errorf(
|
||||
"expected error '%s', got '%s'",
|
||||
code.ErrAmbiguousPackageName.Error(),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePackage_DuplicateStructs(t *testing.T) {
|
||||
_, err := code.ParsePackage(map[string]*ast.Package{
|
||||
"codepkgsuccess": {
|
||||
Files: map[string]*ast.File{
|
||||
"file1.go": goImplFile1,
|
||||
"file2.go": goImplFile2,
|
||||
"file4.go": goImplFile4,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !errors.Is(err, code.DuplicateStructError("User")) {
|
||||
t.Errorf(
|
||||
"expected error '%s', got '%s'",
|
||||
code.ErrAmbiguousPackageName.Error(),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePackage_DuplicateInterfaces(t *testing.T) {
|
||||
_, err := code.ParsePackage(map[string]*ast.Package{
|
||||
"codepkgsuccess": {
|
||||
Files: map[string]*ast.File{
|
||||
"file1.go": goImplFile1,
|
||||
"file2.go": goImplFile2,
|
||||
"file5.go": goImplFile5,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if !errors.Is(err, code.DuplicateInterfaceError("OrderService")) {
|
||||
t.Errorf(
|
||||
"expected error '%s', got '%s'",
|
||||
code.ErrAmbiguousPackageName.Error(),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ func (r FieldReference) ReferencingCode() string {
|
|||
}
|
||||
|
||||
type fieldResolver struct {
|
||||
Structs code.Structs
|
||||
Structs map[string]code.Struct
|
||||
}
|
||||
|
||||
func (r fieldResolver) ResolveStructField(structModel code.Struct, tokens []string) (FieldReference, bool) {
|
||||
|
@ -46,7 +46,7 @@ func (r fieldResolver) ResolveStructField(structModel code.Struct, tokens []stri
|
|||
continue
|
||||
}
|
||||
|
||||
childStruct, ok := r.Structs.ByName(fieldSimpleType.Code())
|
||||
childStruct, ok := r.Structs[fieldSimpleType.Code()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// ParseInterfaceMethod returns repository method spec from declared interface method
|
||||
func ParseInterfaceMethod(structs code.Structs, structModel code.Struct, method code.Method) (MethodSpec, error) {
|
||||
func ParseInterfaceMethod(structs map[string]code.Struct, structModel code.Struct, method code.Method) (MethodSpec, error) {
|
||||
parser := interfaceMethodParser{
|
||||
fieldResolver: fieldResolver{
|
||||
Structs: structs,
|
||||
|
|
|
@ -91,9 +91,9 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
var structs = code.Structs{
|
||||
nameStruct,
|
||||
structModel,
|
||||
var structs = map[string]code.Struct{
|
||||
nameStruct.Name: nameStruct,
|
||||
structModel.Name: structModel,
|
||||
}
|
||||
|
||||
type ParseInterfaceMethodTestCase struct {
|
||||
|
|
31
main.go
31
main.go
|
@ -27,7 +27,7 @@ func main() {
|
|||
flag.Usage = printUsage
|
||||
|
||||
versionPtr := flag.Bool("version", false, "print version of repogen")
|
||||
sourcePtr := flag.String("src", "", "source file")
|
||||
pkgDirPtr := flag.String("pkg", ".", "package directory to scan for model struct and repository interface")
|
||||
destPtr := flag.String("dest", "", "destination file")
|
||||
modelPtr := flag.String("model", "", "model struct name")
|
||||
repoPtr := flag.String("repo", "", "repository interface name")
|
||||
|
@ -39,10 +39,6 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
if *sourcePtr == "" {
|
||||
printUsage()
|
||||
log.Fatal("-src flag required")
|
||||
}
|
||||
if *modelPtr == "" {
|
||||
printUsage()
|
||||
log.Fatal("-model flag required")
|
||||
|
@ -52,7 +48,7 @@ func main() {
|
|||
log.Fatal("-repo flag required")
|
||||
}
|
||||
|
||||
code, err := generateFromRequest(*sourcePtr, *modelPtr, *repoPtr)
|
||||
code, err := generateFromRequest(*pkgDirPtr, *modelPtr, *repoPtr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -84,33 +80,40 @@ func printVersion() {
|
|||
fmt.Println(version)
|
||||
}
|
||||
|
||||
func generateFromRequest(fileName, structModelName, repositoryInterfaceName string) (string, error) {
|
||||
func generateFromRequest(pkgDir, structModelName, repositoryInterfaceName string) (string, error) {
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
|
||||
dir, err := parser.ParseDir(fset, pkgDir, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
file := code.ExtractComponents(f)
|
||||
pkg, err := code.ParsePackage(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
structModel, ok := file.Structs.ByName(structModelName)
|
||||
return generateRepository(pkg, structModelName, repositoryInterfaceName)
|
||||
}
|
||||
|
||||
func generateRepository(pkg code.Package, structModelName, repositoryInterfaceName string) (string, error) {
|
||||
structModel, ok := pkg.Structs[structModelName]
|
||||
if !ok {
|
||||
return "", errors.New("struct model not found")
|
||||
}
|
||||
|
||||
intf, ok := file.Interfaces.ByName(repositoryInterfaceName)
|
||||
intf, ok := pkg.Interfaces[repositoryInterfaceName]
|
||||
if !ok {
|
||||
return "", errors.New("interface model not found")
|
||||
}
|
||||
|
||||
var methodSpecs []spec.MethodSpec
|
||||
for _, method := range intf.Methods {
|
||||
methodSpec, err := spec.ParseInterfaceMethod(file.Structs, structModel, method)
|
||||
methodSpec, err := spec.ParseInterfaceMethod(pkg.Structs, structModel, method)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
methodSpecs = append(methodSpecs, methodSpec)
|
||||
}
|
||||
|
||||
return generator.GenerateRepository(file.PackageName, structModel, intf.Name, methodSpecs)
|
||||
return generator.GenerateRepository(pkg.Name, structModel, intf.Name, methodSpecs)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue