-
-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: including package path in implicit service name #13
base: master
Are you sure you want to change the base?
Conversation
During use, I found that registering services with the same name will panic, even if they came from different packages. Here's an minimal repro: ```go // a/c/a-c.go package c import ( /* ... */ ) type MyStruct struct { /* ... */ } func NewMyStruct(i *do.Injector) (*MyStruct, error) { /* ... */ } // b/c/b-c.go package c import ( /* ... */ ) type MyStruct struct { /* ... */ } func NewMyStruct(i *do.Injector) (*MyStruct, error) { /* ... */ } // main.go package main import ( /* ... */ ) func main() { i := do.New() do.Provide(i, ac.NewMyStruct) do.Provide(i, bc.NewMyStruct) } // panic: DI: service `*c.MyStruct` has already been declared ``` The problem occurs because `generateServiceName()` didn't create a "global-unique" qualifier for type. This PR refactors `generateServiceName()` to create unique qualifiers by prepending package path. \## Implementation This implementation uses `reflect.TypeOf()`, which is the same as `fmt.Sprintf("%T")`. It basically the same as `typeOfT.PkgPath() + "." + typeOfT.Name()`. For the following code it generates such qualifiers: ```go // service_test.go type testStruct struct{} type testInterface interface{} ``` type|qualifier -|- `testStruct`|`github.com/samber/do.testStruct` `*testStruct`|`github.com/samber/do.*testStruct` `testInterface`|`github.com/samber/do.*testInterface` `int`|`int` `*int`|`*int` \## Alternatives \### `typeOfT.Name()` vs. `typeOfT.String()` `fmt.Sprintf("%T")` uses `typeOfT.String()`. But `typeOfT.String()` [says](https://pkg.go.dev/reflect#Type) "The string representation **may** use shortened package names and is not guaranteed to be unique among types." Therefore it cannot be guaranteed for usage as a type qualifier. \### Processing method of pointer indirect star The current `do.*test` looks strange but I haven't thought of a better one yet. \## Testing Tests have all been updated. Maybe it's necessary to create two test packages to test bugs mentioned above, but adding two new files for this is somewhat weird so I didn't do that. Is it necessary? \## Compatibility This doesn't introduce API changes and therefore it should be possible to add into v1. But it introduces internal behavior changes so please consider it carefully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this first contribution.
Do you know in what case typeOfT.String()
might break?
I think we can create some packages under test/fixture/*/*
.
service.go
Outdated
if name != "<nil>" { | ||
return name | ||
if typeOfT == nil { | ||
typeOfT = reflect.TypeOf(new(T)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reflect.TypeOf(&t)
no need to allocate here ;)
Downgrading PR to draft due to design defects. I did some research and found some other problems of this PR.
|
Codecov Report
@@ Coverage Diff @@
## master #13 +/- ##
==========================================
+ Coverage 91.16% 91.60% +0.44%
==========================================
Files 6 6
Lines 396 405 +9
==========================================
+ Hits 361 371 +10
+ Misses 26 25 -1
Partials 9 9
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
Hi @ilharp 👋 I added a commit for supporting array and slice. Map, Func and Chan are still missing. name = generateServiceName[*[]*[]**int]()
is.Equal("*[]*[]**int", name) I will try to push it to do@v2 (with this code moved to a dedicated pkg eventually) |
I made a dedicated pkg with 95% type coverage -> https://github.com/samber/go-type-to-string |
Thanks for this great work! I've used it in my open source project.
During use, I found that registering services with the same name will panic, even if they came from different packages. Here's a minimal repro:
The problem occurs because
generateServiceName()
didn't create a "global-unique" qualifier for type. This PR refactorsgenerateServiceName()
to create unique qualifiers by prepending package path.Implementation
This implementation uses
reflect.TypeOf()
, which is the same asfmt.Sprintf("%T")
. It basically the same astypeOfT.PkgPath() + "." + typeOfT.Name()
.For the following code it generates such qualifiers:
testStruct
github.com/samber/do.testStruct
*testStruct
github.com/samber/do.*testStruct
testInterface
github.com/samber/do.*testInterface
int
int
*int
*int
Alternatives
typeOfT.Name()
vs.typeOfT.String()
fmt.Sprintf("%T")
usestypeOfT.String()
. ButtypeOfT.String()
says "The string representation may use shortened package names and is not guaranteed to be unique among types." Therefore it cannot be guaranteed for usage as a type qualifier.Processing method of pointer indirect star
The current
do.*test
looks strange but I haven't thought of a better one yet.Testing
Tests have all been updated. Maybe it's necessary to create two test packages to test bugs mentioned above, but adding two new files for this is somewhat weird so I didn't do that. Is it necessary?
Compatibility
This doesn't introduce API changes and therefore it should be possible to add into v1. But it introduces internal behavior changes so please consider it carefully.