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

Proposal: Add UserType to process method of TypeProcessor for more flexible code generation #322

Open
timvlaer opened this issue Sep 3, 2019 · 2 comments

Comments

@timvlaer
Copy link
Contributor

timvlaer commented Sep 3, 2019

I'm writing a TypeProcessor that adds a method in generated code for a thrift union type.
Since the processor only gets the generated class as input parameter, it's rather hard to figure out if the generated Thrifty code reflects a union in thrift.

I came up with the following which seems rather fragile to me:

boolean isUnion = type.typeSpecs.stream()
        .filter(e -> "Builder".equals(e.name))
        .findFirst()
        .flatMap(t -> t.methodSpecs.stream().filter(m -> "build".equals(m.name)).findFirst())
        .map(m -> m.code.toString().contains("Invalid union;"))
        .orElse(false);

Would it be a good idea to add the userType as a second argument in the process method? It would make code generation in general a lot easier since generated code can be based upon the thrift schema AST instead of the generated code.

I can draft a PR if this seems a good idea. Changing the method will break the TypeProcessor api.

@benjamin-bader
Copy link
Collaborator

It's a nice idea! Upsides are, obviously, this use case becomes simple, as do a number of structurally-similar problems I've run in to myself.

The downside is that it couples processor implementations to specific Thrifty versions, which hasn't historically been true. We've been less careful about backwards-compatibility with UserType and friends than we have with the runtime types.

On the whole that particular tradeoff seems OK to me, but it isn't quite as simple as adding a parameter to the processor interface and calling it a day. There are more than structs and unions that get passed in - IIRC, services, enums, and even constant-holding classes become TypeSpec objects that get processed, so there isn't necessarily a UserType, or even a single thrift entity, that corresponds with the TypeSpec seen by the processor.

Given that, what do you think a more-capable processor interface should look like?

@timvlaer
Copy link
Contributor Author

timvlaer commented Sep 5, 2019

Interesting viewpoint, thanks for the insightful answer.

I was thinking of changing the signature to the following, although I understand it makes the UserType implementations part of to the public api. I cannot judge the impact of that.

TypeSpec process(UserType spec, TypeSpec type);

I overlooked the Constants class which is indeed directly generated without UserType. I think that's the only case where code is generated without a UserType. I don't know if it makes any sense to create a ConstantsType, just thinking out loud.

An alternative could be to annotate the generated code with references to the original thrift idl. I see however two drawbacks to this approach: it's either thrift idl in comments which requires the processors to basically re-parse the definition. Or it's an annotation or something but that's probably never as complete as the original definition.

What do you think?

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

No branches or pull requests

2 participants