diff --git a/function.go b/function.go index e893461..d21423f 100644 --- a/function.go +++ b/function.go @@ -7,11 +7,11 @@ import ( "log" ) -type Functions []lo.Tuple2[string, []string] +type Functions []internal.Function func FunctionsOfType(fTypName string) Functions { typ, ok := internal.Arch().Type(fTypName) - if !ok || !typ.Func() { + if !ok || !typ.FuncType() { log.Fatalf("can not find function type %s", fTypName) } lo.ForEach(lo.Filter(internal.Arch().Packages(), func(pkg *internal.Package, _ int) bool { @@ -56,7 +56,11 @@ func (functions Functions) ShouldBeInPackage(pkgPath ...string) error { panic("to be implemented") } -func (functions Functions) ShouldBe(visibility Visibility) error { +func (functions Functions) ShouldBe(visibility Visible) error { + panic("to be implemented") +} + +func (functions Functions) LineOfCodeLessThan(n int) error { panic("to be implemented") } diff --git a/go.mod b/go.mod index f5da9a8..b453552 100644 --- a/go.mod +++ b/go.mod @@ -15,14 +15,16 @@ require ( github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kcmvp/gob v1.0.17 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -30,7 +32,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect @@ -42,5 +44,6 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3444f74..87d37c2 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,9 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -27,15 +28,22 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kcmvp/gob v1.0.17 h1:yjin/Y59rHbQ4WpxV4CNaR0TgBBMH54Y8Sm275a0XNs= +github.com/kcmvp/gob v1.0.17/go.mod h1:uFlwrOYFuRBw1mRKBtMNYHcM/64L98qyiNpRv9RM75Y= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -50,8 +58,9 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -94,8 +103,9 @@ golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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= 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= diff --git a/gob.yaml b/gob.yaml index 94819c5..1c27763 100644 --- a/gob.yaml +++ b/gob.yaml @@ -9,7 +9,7 @@ plugins: golangci-lint: alias: lint args: run ./... - url: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 + url: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.58.2 gotestsum: alias: test args: --format testname -- -coverprofile=target/cover.out ./... diff --git a/internal/artifact.go b/internal/artifact.go index 07c786d..479f6c4 100644 --- a/internal/artifact.go +++ b/internal/artifact.go @@ -43,7 +43,7 @@ type Function struct { } type Type struct { - raw *types.Named + raw *types.TypeName } type Variable struct { @@ -121,8 +121,8 @@ func parse(pkg *packages.Package, mode ParseMode) *Package { } case *types.TypeName: if ParseTyp&mode == ParseTyp { - if namedType, ok := vType.Type().(*types.Named); ok { - archPkg.types = append(archPkg.types, Type{raw: namedType}) + if _, ok := vType.Type().(*types.Named); ok { + archPkg.types = append(archPkg.types, Type{raw: vType}) } } case *types.Var: @@ -181,7 +181,7 @@ func (artifact *Artifact) Type(typName string) (Type, bool) { } } return lo.Find(pkg.(*Package).types, func(typ Type) bool { - return typ.raw.String() == typName + return typ.Name() == typName }) } @@ -222,42 +222,47 @@ func (pkg *Package) Path() string { } func (typ Type) Interface() bool { - _, ok := typ.raw.Underlying().(*types.Interface) + _, ok := typ.Raw().Underlying().(*types.Interface) return ok } func (typ Type) Package() string { - return typ.raw.Obj().Pkg().Path() + return typ.Raw().Obj().Pkg().Path() } -func (typ Type) Func() bool { - panic("not implemented") +func (typ Type) FuncType() bool { + _, ok := typ.raw.Type().Underlying().(*types.Signature) + return ok } func (typ Type) Raw() *types.Named { - return typ.raw + return typ.raw.Type().(*types.Named) } func (typ Type) Name() string { - return typ.raw.String() + return typ.Raw().String() } func (typ Type) GoFile() string { - return Arch().Package(typ.Package()).raw.Fset.Position(typ.raw.Obj().Pos()).Filename + return Arch().Package(typ.Package()).raw.Fset.Position(typ.Raw().Obj().Pos()).Filename +} + +func (typ Type) Exported() bool { + return typ.raw.Exported() } func (typ Type) Methods() []Function { var functions []Function if typ.Interface() { - iTyp := typ.raw.Underlying().(*types.Interface) + iTyp := typ.Raw().Underlying().(*types.Interface) n := iTyp.NumMethods() for i := 0; i < n; i++ { functions = append(functions, Function{raw: iTyp.Method(i)}) } } else { - n := typ.raw.NumMethods() + n := typ.Raw().NumMethods() for i := 0; i < n; i++ { - functions = append(functions, Function{raw: typ.raw.Method(i)}) + functions = append(functions, Function{raw: typ.Raw().Method(i)}) } } return functions diff --git a/internal/artifact_test.go b/internal/artifact_test.go index 8a383f5..b7ff390 100644 --- a/internal/artifact_test.go +++ b/internal/artifact_test.go @@ -130,12 +130,14 @@ func TestPackage_Functions(t *testing.T) { "FunctionsOfType", "HavePrefix", "HaveSuffix", - "Lay", - "AllTypes", - "AllPackages", + "LayerByPath", + "AppTypes", + "AppPackages", "SourceNameShould", "TypesEmbeddedWith", "TypesImplement", + "TypesWith", + "PackageByPath", }, imports: []string{ "fmt", @@ -342,3 +344,38 @@ func TestArtifact(t *testing.T) { assert.NotEmpty(t, Arch().RootDir()) assert.Equal(t, "github.com/kcmvp/archunit", Arch().Module()) } + +func TestArchType(t *testing.T) { + size := len(arch.Packages()) + typ, ok := Arch().Type("github.com/samber/lo.Entry[K comparable, V any]") + assert.True(t, ok) + assert.Equal(t, "github.com/samber/lo.Entry[K comparable, V any]", typ.Name()) + assert.True(t, len(arch.Packages()) > size) +} + +func TestArchFuncType(t *testing.T) { + tests := []struct { + name string + typ string + exp bool + }{ + { + name: "valid", + typ: "internal/sample/controller.CustomizeHandler", + exp: true, + }, + { + name: "invalid", + typ: "internal/sample/controller.EmbeddedGroup", + exp: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + typ, ok := Arch().Type(test.typ) + assert.True(t, ok) + assert.Equal(t, test.exp, typ.FuncType()) + }) + } +} diff --git a/internal/sample/controller/handlers.go b/internal/sample/controller/handlers.go index 7cbefb9..82d0482 100644 --- a/internal/sample/controller/handlers.go +++ b/internal/sample/controller/handlers.go @@ -7,6 +7,8 @@ import ( "github.com/gin-gonic/gin" ) +type CustomizeHandler func(c *gin.Context) error + var SayHello gin.HandlerFunc = func(c *gin.Context) {} func LoginHandler(ctx gin.Context) { diff --git a/layer.go b/layer.go index dcea55b..57415df 100644 --- a/layer.go +++ b/layer.go @@ -11,10 +11,10 @@ import ( "strings" ) -type Visibility int +type Visible int const ( - Public Visibility = iota + Public Visible = iota Private ) @@ -46,20 +46,6 @@ func SourceNameShould(pattern NamePattern, args ...string) error { return nil } -//func MethodsOfTypeShouldBeDefinedInSameFile() error { -// for _, pkg := range internal.Arch().Packages() { -// for _, typ := range pkg.Types() { -// files := lo.Uniq(lo.Map(typ.Methods(), func(f internal.Function, _ int) string { -// return f.GoFile() -// })) -// if len(files) > 1 { -// return fmt.Errorf("methods of type %s are defined in files %v", typ.Name(), files) -// } -// } -// } -// return nil -//} - func ConstantsShouldBeDefinedInOneFileByPackage() error { for _, pkg := range internal.Arch().Packages() { files := pkg.ConstantFiles() @@ -70,7 +56,7 @@ func ConstantsShouldBeDefinedInOneFileByPackage() error { return nil } -func Lay(pkgPaths ...string) Layer { +func LayerByPath(pkgPaths ...string) Layer { patterns := lo.Map(pkgPaths, func(path string, _ int) *regexp.Regexp { reg, err := internal.PkgPattern(path) if err != nil { @@ -130,6 +116,18 @@ func (layer Layer) Sub(name string, paths ...string) Layer { }) } +func (layer Layer) Packages() Packages { + return Packages(layer) +} + +func (layer Layer) Functions() Functions { + var fs Functions + lo.ForEach(layer, func(pkg *internal.Package, _ int) { + fs = append(fs, pkg.Functions()...) + }) + return fs +} + func (layer Layer) packages() []string { return lo.Map(layer, func(item *internal.Package, _ int) string { return item.Path() @@ -156,7 +154,7 @@ func (layer Layer) ShouldNotReferLayers(layers ...Layer) error { } func (layer Layer) ShouldNotReferPackages(paths ...string) error { - return layer.ShouldNotReferLayers(Lay(paths...)) + return layer.ShouldNotReferLayers(LayerByPath(paths...)) } func (layer Layer) ShouldOnlyReferLayers(layers ...Layer) error { @@ -171,7 +169,7 @@ func (layer Layer) ShouldOnlyReferLayers(layers ...Layer) error { } func (layer Layer) ShouldOnlyReferPackages(paths ...string) error { - return layer.ShouldOnlyReferLayers(Lay(paths...)) + return layer.ShouldOnlyReferLayers(LayerByPath(paths...)) } func (layer Layer) ShouldBeOnlyReferredByLayers(layers ...Layer) error { @@ -195,7 +193,7 @@ func (layer Layer) ShouldBeOnlyReferredByLayers(layers ...Layer) error { } func (layer Layer) ShouldBeOnlyReferredByPackages(paths ...string) error { - layer1 := Lay(paths...) + layer1 := LayerByPath(paths...) return layer.ShouldBeOnlyReferredByLayers(layer1) } @@ -210,7 +208,11 @@ func (layer Layer) DepthShouldLessThan(depth int) error { } func (layer Layer) Types() Types { - panic("to be implemented") + var ts Types + lo.ForEach(layer, func(pkg *internal.Package, _ int) { + ts = append(ts, pkg.Types()...) + }) + return ts } func (layer Layer) Files() Files { diff --git a/layer_test.go b/layer_test.go index 0a5169b..1f9afbd 100644 --- a/layer_test.go +++ b/layer_test.go @@ -1,6 +1,8 @@ package archunit import ( + "github.com/kcmvp/archunit/internal" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "strings" "testing" @@ -20,7 +22,7 @@ func TestPackages(t *testing.T) { size1: 1, }, { - name: "sample and sub Lay", + name: "sample and sub LayerByPath", paths: []string{".../internal/sample/..."}, except: []string{".../ext"}, size1: 12, @@ -39,7 +41,7 @@ func TestPackages(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - layer := Lay(test.paths...) + layer := LayerByPath(test.paths...) assert.Equal(t, test.size1, len(layer.packages())) if len(test.except) > 0 { layer = layer.Exclude(test.except...) @@ -74,7 +76,7 @@ func TestLayer_Sub(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - layer := Lay(tt.paths...) + layer := LayerByPath(tt.paths...) assert.Equal(t, tt.size1, len(layer.packages())) layer = layer.Sub(tt.name, tt.sub...) assert.Equal(t, tt.size2, len(layer.packages())) @@ -95,7 +97,7 @@ func TestConstantsShouldBeDefinedInOneFileByPackage(t *testing.T) { } func TestLayPackages(t *testing.T) { - layer := Lay("sample/controller", "sample/controller/...") + layer := LayerByPath("sample/controller", "sample/controller/...") assert.ElementsMatch(t, []string{"github.com/kcmvp/archunit/internal/sample/controller", "github.com/kcmvp/archunit/internal/sample/controller/module1"}, layer.packages()) assert.ElementsMatch(t, layer.Imports(), @@ -109,10 +111,10 @@ func TestLayPackages(t *testing.T) { } func TestLayer_Refer(t *testing.T) { - controller := Lay("sample/controller", "sample/controller/...") - model := Lay("sample/model") - service := Lay("sample/service", "sample/service/...") - repository := Lay("sample/repository", "sample/repository/...") + controller := LayerByPath("sample/controller", "sample/controller/...") + model := LayerByPath("sample/model") + service := LayerByPath("sample/service", "sample/service/...") + repository := LayerByPath("sample/repository", "sample/repository/...") assert.NoError(t, controller.ShouldNotReferLayers(model)) assert.NoError(t, controller.ShouldNotReferPackages("sample/model")) assert.Errorf(t, controller.ShouldNotReferLayers(repository), "controller should not refer repository") @@ -121,4 +123,19 @@ func TestLayer_Refer(t *testing.T) { assert.NoError(t, repository.ShouldOnlyReferPackages("sample/model"), "repository should not refer model") assert.NoError(t, model.ShouldBeOnlyReferredByLayers(repository), "model should be only referred repository") assert.Error(t, repository.ShouldBeOnlyReferredByLayers(service), "repository is referenced by controller") + assert.ElementsMatch(t, controller.packages(), []string{"github.com/kcmvp/archunit/internal/sample/controller", + "github.com/kcmvp/archunit/internal/sample/controller/module1"}) + assert.ElementsMatch(t, lo.Map(controller.Types(), func(typ internal.Type, index int) string { + return typ.Name() + }), []string{ + "github.com/kcmvp/archunit/internal/sample/controller.CustomizeHandler", + "github.com/kcmvp/archunit/internal/sample/controller.EmbeddedGroup", + "github.com/kcmvp/archunit/internal/sample/controller.GroupWithNonEmbedded", + "github.com/kcmvp/archunit/internal/sample/controller.LoginController", + "github.com/kcmvp/archunit/internal/sample/controller.MyRouterGroup", + "github.com/kcmvp/archunit/internal/sample/controller/module1.AppController", + }) + assert.ElementsMatch(t, lo.Map(controller.Functions(), func(item internal.Function, index int) string { + return item.Name() + }), []string{"LoginHandler"}) } diff --git a/package.go b/package.go index c09716c..31c718d 100644 --- a/package.go +++ b/package.go @@ -10,8 +10,14 @@ import ( type Packages []*internal.Package -func AllPackages() Packages { - return internal.Arch().Packages() +func AppPackages() Packages { + return lo.Filter(internal.Arch().Packages(), func(pkg *internal.Package, _ int) bool { + return strings.HasPrefix(pkg.ID(), internal.Arch().Module()) + }) +} + +func PackageByPath(paths ...string) Packages { + panic("not implemented") } func (pkgs Packages) Paths() []string { @@ -28,6 +34,18 @@ func (pkgs Packages) Skip(paths ...string) Packages { }) } +func (pkgs Packages) Types() Types { + panic("@todo") +} + +func (pkgs Packages) Functions() Functions { + panic("@todo") +} + +func (pkgs Packages) Files() Files { + panic("@todo") +} + func (pkgs Packages) NameShouldBeSameAsFolder() error { result := lo.FilterMap(pkgs, func(pkg *internal.Package, _ int) (string, bool) { return pkg.Path(), !strings.HasSuffix(pkg.Path(), pkg.Name()) diff --git a/package_test.go b/package_test.go index e7b65b8..3e947cf 100644 --- a/package_test.go +++ b/package_test.go @@ -7,7 +7,7 @@ import ( ) func TestPackages_NameShouldBeSameAsFolder(t *testing.T) { - pkgs := AllPackages() + pkgs := AppPackages() assert.Equal(t, 15, len(pkgs)) err := pkgs.NameShouldBeSameAsFolder() assert.Error(t, err) @@ -21,8 +21,8 @@ func TestPackages_NameShouldBeSameAsFolder(t *testing.T) { } func TestPackageNameShould(t *testing.T) { - err := AllPackages().NameShould(BeLowerCase) + err := AppPackages().NameShould(BeLowerCase) assert.NoError(t, err) - err = AllPackages().NameShould((BeUpperCase)) + err = AppPackages().NameShould((BeUpperCase)) assert.Error(t, err) } diff --git a/type.go b/type.go index 542d817..6be333f 100644 --- a/type.go +++ b/type.go @@ -14,14 +14,22 @@ import ( type Types []internal.Type -func AllTypes() Types { +// AppTypes return all the types defined in the project +func AppTypes() Types { var typs Types for _, pkg := range internal.Arch().Packages() { - typs = append(typs, pkg.Types()...) + if strings.HasPrefix(pkg.ID(), internal.Arch().Module()) { + typs = append(typs, pkg.Types()...) + } } return typs } +func TypesWith(typeNames ...string) Types { + panic("") +} + +// TypesEmbeddedWith returns all the types that embed the specified type func TypesEmbeddedWith(embeddedType string) Types { eType, ok := internal.Arch().Type(embeddedType) if !ok { @@ -48,7 +56,6 @@ func TypesEmbeddedWith(embeddedType string) Types { return true }) return typs - } // TypesImplement return all the types implement the interface @@ -76,27 +83,63 @@ func TypesImplement(interName string) Types { return typs } -func (typs Types) Skip(names ...string) Types { - panic("to be implemented") +// Skip filter out the specified types +func (types Types) Skip(typNames ...string) Types { + return lo.Filter(types, func(typ internal.Type, _ int) bool { + return !lo.Contains(typNames, typ.Name()) + }) } -func (typs Types) EmbeddedWith(embeds ...string) Types { - panic("to be implemented") +// EmbeddedWith return types that embed the specified types +func (types Types) EmbeddedWith(embedTyps ...string) Types { + embedded := lo.Map(embedTyps, func(typName string, _ int) internal.Type { + t, ok := internal.Arch().Type(typName) + if !ok { + log.Fatalf("can not find type %s", typName) + } + return t + }) + return lo.Filter(types, func(typ internal.Type, _ int) bool { + return lo.Contains(embedded, typ) + }) } -func (typs Types) Implement(inters ...string) Types { - panic("to be implemented") +func (types Types) Implement(interTyps ...string) Types { + inters := lo.Map(interTyps, func(typName string, _ int) internal.Type { + t, ok := internal.Arch().Type(typName) + if !ok { + log.Fatalf("can not find type %s", typName) + } + return t + }) + return lo.Filter(types, func(typ internal.Type, _ int) bool { + return lo.Contains(inters, typ) + }) } -func (typs Types) InPackage(paths ...string) Types { - panic("to be implemented") +// InPackages return types in the specified packages +func (types Types) InPackages(paths ...string) Types { + return lo.Filter(types, func(typ internal.Type, _ int) bool { + return lo.ContainsBy(paths, func(path string) bool { + return strings.HasSuffix(typ.Package(), path) + }) + }) } -func (typs Types) Functions() []Functions { - panic("to be implemented") +// Methods return all the methods of the types +func (types Types) Methods() Functions { + var functions Functions + lop.ForEach(types, func(typ internal.Type, _ int) { + functions = append(functions, typ.Methods()...) + }) + return functions } -func (typs Types) ShouldBeDefinedInSameFile() error { +func (types Types) Files() Files { + panic("") +} + +func (types Types) MethodShouldBeDefinedInOneFile() error { for _, pkg := range internal.Arch().Packages() { for _, typ := range pkg.Types() { files := lo.Uniq(lo.Map(typ.Methods(), func(f internal.Function, _ int) string { @@ -110,14 +153,32 @@ func (typs Types) ShouldBeDefinedInSameFile() error { return nil } -func (typs Types) ShouldBe(visibility Visibility) error { - panic("to be implemented") +// ShouldBe check the types' visibility. return an error when any type is not the specified Visible +func (types Types) ShouldBe(visible Visible) error { + if t, ok := lo.Find(types, func(typ internal.Type) bool { + return visible != lo.If(typ.Exported(), Public).Else(Private) + }); ok { + return fmt.Errorf("type %s is %s", t.Name(), lo.If(t.Exported(), "public").Else("private")) + } + return nil } -func (typs Types) ShouldBeInPackages(pkgs ...string) error { - panic("to be implemented") +func (types Types) ShouldBeInPackages(pkgs ...string) error { + if t, ok := lo.Find(types, func(typ internal.Type) bool { + return !lo.Contains(pkgs, typ.Package()) + }); ok { + return fmt.Errorf("type is %s in %s", t.Name(), t.Package()) + } + return nil } -func (typs Types) NameShould(pattern NamePattern) error { - panic("to be implemented") +func (types Types) NameShould(pattern NamePattern, args ...string) error { + if t, ok := lo.Find(types, func(typ internal.Type) bool { + return !pattern(typ.Name(), lo.If(args == nil, "").ElseF(func() string { + return args[0] + })) + }); ok { + return fmt.Errorf("Type %s faild to pass naming checking", t.Name()) + } + return nil } diff --git a/type_test.go b/type_test.go index a33b8d0..e7f1f00 100644 --- a/type_test.go +++ b/type_test.go @@ -8,7 +8,8 @@ import ( ) func TestAllTypes(t *testing.T) { - typs := lo.Map(AllTypes(), func(item internal.Type, _ int) string { + allTypes := AppTypes() + typs := lo.Map(allTypes, func(item internal.Type, _ int) string { return item.Name() }) expected := []string{ @@ -31,7 +32,7 @@ func TestAllTypes(t *testing.T) { "github.com/kcmvp/archunit.NamePattern", "github.com/kcmvp/archunit.Packages", "github.com/kcmvp/archunit.Types", - "github.com/kcmvp/archunit.Visibility", + "github.com/kcmvp/archunit.Visible", "github.com/kcmvp/archunit/internal/sample/views.UserView", "github.com/kcmvp/archunit/internal/sample/controller.LoginController", "github.com/kcmvp/archunit/internal/sample/service.Audit", @@ -47,6 +48,7 @@ func TestAllTypes(t *testing.T) { "github.com/kcmvp/archunit/internal/sample/controller.MyRouterGroup", "github.com/kcmvp/archunit/internal/sample/controller.EmbeddedGroup", "github.com/kcmvp/archunit/internal/sample/controller.GroupWithNonEmbedded", + "github.com/kcmvp/archunit/internal/sample/controller.CustomizeHandler", } assert.ElementsMatch(t, expected, typs) } @@ -105,3 +107,106 @@ func TestTypesEmbeddedWith(t *testing.T) { }) } } + +func TestTypes_Skip(t *testing.T) { + allTypes := AppTypes() + tests := []struct { + name string + typeNames []string + num int + }{ + { + name: "skip_internal.Type", + typeNames: []string{"github.com/kcmvp/archunit/internal.Type"}, + num: 35, + }, + { + name: "skip_internal.Type_archunit.File", + typeNames: []string{ + "github.com/kcmvp/archunit/internal.Type", + "github.com/kcmvp/archunit.File", + }, + num: 34, + }, + { + name: "skip_internal.Type_archunit.File_service.Audit", + typeNames: []string{ + "github.com/kcmvp/archunit/internal.Type", + "github.com/kcmvp/archunit.File", + "github.com/kcmvp/archunit/internal/sample/service.Audit", + }, + num: 33, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + remains := lo.Map(allTypes.Skip(test.typeNames...), func(item internal.Type, _ int) string { + return item.Name() + }) + assert.Len(t, remains, test.num) + assert.NotContains(t, remains, test.typeNames) + }) + } +} + +func TestTypes_InPackages(t *testing.T) { + allTypes := AppTypes() + tests := []struct { + name string + pkgs []string + typs []string + }{ + { + name: "internal", + pkgs: []string{"archunit/internal"}, + typs: []string{ + "github.com/kcmvp/archunit/internal.Artifact", + "github.com/kcmvp/archunit/internal.Function", + "github.com/kcmvp/archunit/internal.Package", + "github.com/kcmvp/archunit/internal.Param", + "github.com/kcmvp/archunit/internal.ParseMode", + "github.com/kcmvp/archunit/internal.Type", + "github.com/kcmvp/archunit/internal.Variable", + }, + }, + { + name: "kcmvp/internal", + pkgs: []string{"kcmvp/archunit/internal"}, + typs: []string{ + "github.com/kcmvp/archunit/internal.Artifact", + "github.com/kcmvp/archunit/internal.Function", + "github.com/kcmvp/archunit/internal.Package", + "github.com/kcmvp/archunit/internal.Param", + "github.com/kcmvp/archunit/internal.ParseMode", + "github.com/kcmvp/archunit/internal.Type", + "github.com/kcmvp/archunit/internal.Variable", + }, + }, + { + name: "kcmvp/internal&controller", + pkgs: []string{"archunit/internal", "internal/sample/controller"}, + typs: []string{ + "github.com/kcmvp/archunit/internal.Artifact", + "github.com/kcmvp/archunit/internal.Function", + "github.com/kcmvp/archunit/internal.Package", + "github.com/kcmvp/archunit/internal.Param", + "github.com/kcmvp/archunit/internal.ParseMode", + "github.com/kcmvp/archunit/internal.Type", + "github.com/kcmvp/archunit/internal.Variable", + "github.com/kcmvp/archunit/internal/sample/controller.EmbeddedGroup", + "github.com/kcmvp/archunit/internal/sample/controller.GroupWithNonEmbedded", + "github.com/kcmvp/archunit/internal/sample/controller.LoginController", + "github.com/kcmvp/archunit/internal/sample/controller.MyRouterGroup", + "github.com/kcmvp/archunit/internal/sample/controller.CustomizeHandler", + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + typs := allTypes.InPackages(test.pkgs...) + assert.ElementsMatch(t, test.typs, lo.Map(typs, func(item internal.Type, _ int) string { + return item.Name() + })) + }) + } +}