Skip to content
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

Add support for func-types to fx.As() #1249

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

pelmennoteam
Copy link

No description provided.

@CLAassistant
Copy link

CLAassistant commented Nov 29, 2024

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@JacobOaks JacobOaks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I have a couple comments on the implementation.

At a high level, I can't think of any particular reason why this shouldn't be allowed, but can you maybe provide some insight into why this is desired over, say, just providing a struct with a call-able method defined on it and using fx.As(new(someInterface))? Is it just cleaner in some scenarios?

Comment on lines +1282 to +1286
if !((at.types[i].typ.Kind() == reflect.Interface && t.Implements(at.types[i].typ)) ||
t.ConvertibleTo(at.types[i].typ)) {
return nil,
nil,
fmt.Errorf("invalid fx.As: %v does not implement or is not convertible to %v", t, at.types[i])
Copy link
Contributor

@JacobOaks JacobOaks Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make these two separate checks? This will keep the code cleaner and give better error messages.

if at.types[i].typ.Kind() == reflect.Interface {
    if !t.Implements(at.types[i].typ) {
        return nil, nil, fmt.Errorf("invalid fx.As: %v does not implement %v", t, at.types[i])
    }
} else if !t.ConvertibleTo(at.types[i].typ) {
        return nil, nil, fmt.Errorf("invalid fx.As: %v cannot be converted to %v", t, at.types[i])
}

Comment on lines +1320 to +1324
if newOutResult.Field(i).Kind() == reflect.Func {
newOutResult.Field(i).Set(getResult(i, results).Convert(newOutResult.Field(i).Type()))
} else {
newOutResult.Field(i).Set(getResult(i, results))
}
Copy link
Contributor

@JacobOaks JacobOaks Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we flip these conditions to make the control flow more/appear consistent with the check above?

if newOutResult.Field(i).Kind() == reflect.Interface {
    newOutResult.Field(i).Set(getResult(i, results))
} else {
    newOutResult.Field(i).Set(getResult(i, results).Convert(newOutResult.Field(i).Type()))
}

@@ -1300,7 +1317,11 @@ func (at *asAnnotation) results(ann *annotated) (

newOutResult := reflect.New(resType).Elem()
for i := 1; i < resType.NumField(); i++ {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we would need to check Value.CanConvert first since technically Value.Convert can panic even if Type.ConvertibleTo returns true? I can't think of any reason why a func value wouldn't be convertible if its type is though.

@@ -477,6 +480,32 @@ func TestAnnotatedAs(t *testing.T) {
assert.Equal(t, s.String(), "another stringer")
},
},
{
desc: "value type convertible to target type",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider downscoping the names of these cases since convertible types are locked down to just functions right now

Suggested change
desc: "value type convertible to target type",
desc: "function value convertible to target type",

},
},
{
desc: "anonymous value type convertible to target type",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a test case for a function vaoue that is not convertible to the target type?

@@ -1145,6 +1146,19 @@ var _ Annotation = (*asAnnotation)(nil)
// constructor does NOT provide both bytes.Buffer and io.Writer type; it just
// provides io.Writer type.
//
// Example for function-types:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update line 1129 as well:

- // constructor) to be provided as another interface.
+ // constructor) to be provided as another type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants