risor-io / risor

Fast and flexible scripting for Go developers and DevOps.
https://risor.io
Apache License 2.0
581 stars 24 forks source link

Generate Risor bindings in builtin modules #159

Closed applejag closed 6 months ago

applejag commented 6 months ago

Writing Risor Go modules (aka builtin modules) is a lot of boilerplate.

This is a proposal to automate some parts of that, by writing our own Go code generator.

To start with, I've only implemented some core features, just so I can try it out. I've also replaced most functions in the strings module with these generated ones. To replace the remaining functions in the strings module too, I'd just have to add support for slice types, which isn't a biggy.

Some features that I did not implement just to keep the initial PR smaller:

Benefits

Drawbacks


Closes #158

myzie commented 6 months ago

This looks really promising!

This solution aims to be opt-in on a per-function basis, so it should not be as intrusive.

I think this is a key point. This should be great as something that's available but not required.

I'll review in more detail. Awesome stuff.

luisdavim commented 6 months ago

I haven't checked the code yet and I like the idea of having a tool to generate the boilerplate, like cobra and kubebuilder do but I'm not so sure about the drawbacks mentioned in the PR description, they seem pretty significant to me and sound like something that would get in the way of my workflow.

applejag commented 6 months ago

I haven't checked the code yet and I like the idea of having a tool to generate the boilerplate, like cobra and kubebuilder do but I'm not so sure about the drawbacks mentioned in the PR description, they seem pretty significant to me and sound like something that would get in the way of my workflow.

I listed the drawback together with counterpoints to the drawbacks in the same bulletpoints.

Go's build system isn't super flexible, so forcing you to run go generate is pretty much the best we can do. It's also the approach taken by lots of other projects, such as gRPC, templ, and kubebuilder as you mentioned.

Regarding the layer of abstraction one, the counterpoint to that one is that if you want full control, then you just implement the function yourself without the generated mapping. The way it's done in this PR means that you can mix and match generated and non-generated functions freely in the same module.

luisdavim commented 6 months ago

I think a bit of duplication and some copy/pasting is better than adding complexity and forcing a specific workflow. As for kubebuilder I was talking specifically about the boilerplate generator part, not the manifest and deep copy generation part and you are not required to run dose on every edit for sure.

I guess this case should be closer to what cobra does for adding new commands, it generates a starting point for you to edit and that's it, I think it would be quite simple to do that for a new module and no changes would be needed on the existing code for that.

myzie commented 6 months ago

It seems like the approach prototyped here doesn't force a specific workflow, in that the module author can choose to opt-in using the risor:generate comment.

Do you agree @luisdavim?

luisdavim commented 6 months ago

It seems like the approach prototyped here doesn't force a specific workflow, in that the module author can choose to opt-in using the risor:generate comment.

Do you agree @luisdavim?

Yes, looks like it. I still think that something that would generate a starting point to be edited would be simpler and more useful.

This approach looks very cool but I think it will end up serving more limited use cases. But we can add that later and I don't see any issues in adding this as it can be useful for those simpleer use cases.

applejag commented 6 months ago

I think the generated code should be committed, and there should be a make target for triggering the code generation.

The generated code is committed, and I've now added two targets: