UniFFI Extensions
A while back when we were discussing decorators, one of the suggestions was to add support for "UniFFI extensions", which could generate additional code. Back then it was very hand-wavey, but maybe it's time to revisit this. I think a couple changes make it possible to discuss concretely:
- All the interface data goes through the
uniffi_metastructs, which are JSON serializable. - The bindings generation is now much more standardized.
Could this work?
- Extensions are binaries that input and output JSON data
- The CLI would get a
--extension=[path-to-executable]flag. Multiple extensions could be specified. - The input data would include:
- The name of the language being generated.
- The list of
uniffi_meta::Metadatavalues. Maybe we could move towards stabilizing this -- meaning we would promise to only add new variants/structs. - A map of type names to
FFIConverternames
- The output data would include:
- A block of code to insert in the generated bindings
- A list of imports to merge into the import list. The schema for this is language-specific, but I think that would be fine. Maybe we could stabilize this for each language.
Some ideas for extensions:
- Generating mocks
- Generating adapter classes, for example wrapping an interface with sync methods to present an async interface.
Thanks for raising this! Should we define a concrete goal for UniFFI extensions? ie, some way of declaring success, or some yardstick to hold up against ideas?
One possibility is something like "avoid the need to have hand-written wrappers around uniffi components". Mocks and async adaptors probably fall into this category. Mozilla's places wrappers seems like a rich vein of ideas too (there we split things based on read-only or read-write capabilities, record telemetry, transform some values, and other things best described as "make a rusty api look more kotliny"). It would be interesting to hear what other uniffi consumers do here (ie, is it a universal experience that wrappers are needed, or have Mozilla just created an overly complex system due to other constraints we have around how we actually ship our components?)
(Another reason I like the "avoid hand-written wrappers" thing is due to the newish docstring capability - having hand-written wrappers be what's ultimately consumed means none of our docstrings are going to be visible to our Android devs. hand-written wrappers also just smells like uniffi limitations rather than conscious decisions)
Maybe that's not quite the correct lens to view this through, but I think a concrete problem statement would help us know if we are heading in the right direction?
Great question. I think "avoid the need to have hand-written wrappers around uniffi components" is a great first draft at a goal. Maybe there are some hand-written wrappers that should be non-goals, for example ones that don't depend on the metadata and the extension would just be copy and pasting the code. I think we can identify those that out as we go.
I also really like the places example. Maybe even better than a goal statement, would be a list of use-cases that we want to address
(Another reason I like the "avoid hand-written wrappers" thing is due to the newish docstring capability - having hand-written wrappers be what's ultimately consumed means none of our docstrings are going to be visible to our Android devs. hand-written wrappers also just smells like uniffi limitations rather than conscious decisions)
I totally agree.