From 78bf9c350c0d73eb2727d4b491aafbc48a586fb7 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Sat, 14 Jan 2023 20:44:22 -0800 Subject: [PATCH 1/3] fix typo in docs for Curry() (#54) --- utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.go b/utils.go index eba4e1a..ad0cea4 100644 --- a/utils.go +++ b/utils.go @@ -5,7 +5,7 @@ import ( "reflect" ) -// Curry generates a Requird Provider that prefills arguments to a function to create a +// Curry generates a Required Provider that prefills arguments to a function to create a // new function that needs fewer args. // // Only arguments with a unique (to the function) type can be curried. From 785a80396e0bf164046fb72380ad9d439b70e17d Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Thu, 19 Jan 2023 20:25:35 -0800 Subject: [PATCH 2/3] document how to force a downstream provider to be included (#55) --- doc.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc.go b/doc.go index 6df8d30..b3ed4d7 100644 --- a/doc.go +++ b/doc.go @@ -500,5 +500,20 @@ defines. Wrapper functions have a small runtime performance penalty, so if you have more than a couple of providers that need cleanup, it makes sense to include something like CleaningService. +Forcing inclusion + +The normal direction of forced inclusion is that an upstream provider is required +because a downstream provider uses a type produced by the upstream provider. + +There are times when the relationship needs to be reversed. For example, a type +gets modified by a downstream injector. The simplest option is to combine the providers +into one function. + +Another possibility is to mark the upstream provider with MustConsume and have it +produce a type that is only consumed by the downstream provider. + +Lastly, the providers can be grouped with Cluster so that they'll be included or +excluded as a group. + */ package nject From bfb98333bd27415e2ed409c1e5f3a29c85023d75 Mon Sep 17 00:00:00 2001 From: David Sharnoff Date: Tue, 31 Jan 2023 22:08:45 -0800 Subject: [PATCH 3/3] add warning about duplicated types to DetailedError (#57) --- error.go | 38 ++++++++++++++++++++++++++++++++++++++ internal/foo/foo.go | 3 +++ internal/foo/v2/foo.go | 3 +++ type_codes.go | 20 ++++++++++++++++++-- type_codes_test.go | 28 ++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 internal/foo/foo.go create mode 100644 internal/foo/v2/foo.go create mode 100644 type_codes_test.go diff --git a/error.go b/error.go index 7b9c482..1d31cfc 100644 --- a/error.go +++ b/error.go @@ -2,6 +2,7 @@ package nject import ( "errors" + "sync" ) type njectError struct { @@ -20,7 +21,44 @@ func (ne *njectError) Error() string { func DetailedError(err error) string { var njectError *njectError if errors.As(err, &njectError) { + dups := duplicateTypes() + if dups != "" { + return err.Error() + "\n\n" + njectError.details + + "\n\nWarning: the following type names refer to more than one type:\n" + + dups + } return err.Error() + "\n\n" + njectError.details } return err.Error() } + +var duplicatesThrough int +var dupLock sync.Mutex +var duplicates string +var duplicatesFound = make(map[string]struct{}) + +func duplicateTypes() string { + max := func() int { + lock.Lock() + defer lock.Unlock() + return typeCounter + }() + dupLock.Lock() + defer dupLock.Unlock() + if duplicatesThrough == max { + return duplicates + } + names := make(map[string]struct{}) + for i := 1; i <= typeCounter; i++ { + n := typeCode(i).String() + if _, ok := names[n]; ok { + if _, ok := duplicatesFound[n]; !ok { + duplicates += " " + n + duplicatesFound[n] = struct{}{} + } + } + names[n] = struct{}{} + } + duplicatesThrough = max + return duplicates +} diff --git a/internal/foo/foo.go b/internal/foo/foo.go new file mode 100644 index 0000000..221b1da --- /dev/null +++ b/internal/foo/foo.go @@ -0,0 +1,3 @@ +package foo + +type Bar struct {} diff --git a/internal/foo/v2/foo.go b/internal/foo/v2/foo.go new file mode 100644 index 0000000..221b1da --- /dev/null +++ b/internal/foo/v2/foo.go @@ -0,0 +1,3 @@ +package foo + +type Bar struct {} diff --git a/type_codes.go b/type_codes.go index 38925c5..fffd9fc 100644 --- a/type_codes.go +++ b/type_codes.go @@ -1,9 +1,12 @@ package nject -// TODO: switch from typeCode to reflect.Type +// TODO: switch from typeCode to reflect.Type -- duplicate detection would be lost import ( + "path" "reflect" + "regexp" + "strings" "sync" ) @@ -73,9 +76,22 @@ func (tc typeCode) Type() reflect.Type { return reverseMap[tc] } +var versionRE = regexp.MustCompile(`/v(\d+)$`) + // Type returns the reflect.Type for this typeCode func (tc typeCode) String() string { - return tc.Type().String() + ts := tc.Type().String() + if versionRE.MatchString(tc.Type().PkgPath()) { + pp := tc.Type().PkgPath() + version := path.Base(pp) + pn := path.Base(path.Dir(pp)) + revised := strings.Replace(ts, pn, pn+"/"+version, 1) + if revised != ts { + return revised + } + return "(" + version + ")" + ts + } + return ts } func (tcs typeCodes) Types() []reflect.Type { diff --git a/type_codes_test.go b/type_codes_test.go new file mode 100644 index 0000000..079770b --- /dev/null +++ b/type_codes_test.go @@ -0,0 +1,28 @@ +package nject + +import ( + "reflect" + "testing" + + v1 "github.com/muir/nject/internal/foo" + v2 "github.com/muir/nject/internal/foo/v2" + + "github.com/stretchr/testify/assert" +) + +func TestVersionedNames(t *testing.T) { + x1 := getTypeCode(v1.Bar{}) + assert.Equal(t, "foo.Bar", x1.String()) + t.Log("base type", reflect.TypeOf(v2.Bar{}).PkgPath()) + assert.Equal(t, "foo/v2.Bar", getTypeCode(v2.Bar{}).String()) + t.Log("pointer", reflect.TypeOf(&v2.Bar{}).PkgPath()) + assert.Equal(t, "*foo.Bar", getTypeCode(&v2.Bar{}).String()) // :( + assert.Equal(t, "*foo.Bar", getTypeCode(&v1.Bar{}).String()) // :( + t.Log("slice", reflect.TypeOf([]v2.Bar{}).PkgPath()) + assert.Equal(t, "[]foo.Bar", getTypeCode([]v2.Bar{}).String()) // :( + assert.Equal(t, "[]foo.Bar", getTypeCode([]v1.Bar{}).String()) // :( + dups := duplicateTypes() + assert.Contains(t, dups, "[]foo.Bar") + assert.Contains(t, dups, "*foo.Bar") + t.Log("duplicates", dups) +}