Hi all, here is an update on the current status of Elixir bindings as of October 2021, and the next steps for developing the library.
*HIGH-LEVEL WORK TO BE DONE*
The
https://github.com/elitau/pact_elixir library is a community-built implementation of Pact in Elixir. Thank you to @pact-slack for the great work on this library so far! In order to make the library fully-featured, we have these general tasks to complete:
? Change the Elixir FFI implementation (see details below)
? Re-implement consumer-side mocks with new FFI
? Implement provider verification
? Implement messaging equivalents
? Implement other workflows like publishing and downloading pacts from a broker
*FFI: PROBLEM AND CONSTRAINTS*
The pact_elixir library currently uses a Elixir+Rust library called
https://github.com/rusterlium/rustler to create safe bindings between the Elixir wrapper and the Rust reference implementation of Pact. Rustler is a great library for creating interop between the two languages because it minimizes the risks of FFI. However, rustler requires the app developer to have a Rust toolchain installed, and to compile the core Rust code at the same time as the app and the pact_elixir library are compiled. This leads to more system dependencies and slower build performance. The developer should not need to know that the internals of pact_elixir are actually written in a different language.
The Elixir and Erlang ecosystem has another FFI concept called NIFs, or Natively-Implemented Functions. Note that NIFs require special instrumentation of the native code in order to communicate properly with the Elixir bindings that are calling in. This does not seem like an ideal solution either, because it requires the pact_elixir library to maintain hand-written shim code in C to add this instrumentation.
*FFI: NEXT STEPS*
We intend to follow the same approach that pact-js and pact-go now follow: the pact_elixir library should download the FFI bundles for each OS architecture at library publish time, and then link to them using whatever strategy is most unobtrusive to the app developer. We can reuse the pipeline scripts from these other implementations in order to download the bundles.
We need to pick an appropriate FFI strategy from the Elixir/Erlang ecosystem to allow a good developer experience. Rustler and handwritten NIF instrumentation do not seem to be good options. Erlang Ports or Port Drivers may be viable options. We may also be able to find a library to automatically *generate NIF bindings around the FFI bundles*, and if this is possible, this may be the best option. I am not very familiar with this part of the Elixir and Erlang ecosystem, so if anyone is more familiar with what is available, please share what you know!
Once we have researched and picked the right FFI strategy, we can create the bindings and have a fully-featured Elixir implementation of Pact!
*RESOURCES*
?
https://github.com/elitau/pact_elixir: Elixir bindings for Pact, let?s focus our efforts to evolve this repo!
?
https://hex.pm: package manager for the ecosystem
?
https://erlang.org/doc/tutorial/users_guide.html: Erlang Interoperability Tutorial User?s Guide, explains NIFs, ports, port drivers, etc.
?
https://sudonull.com/post/97911-How-to-write-your-NIF-in-Elixir: Tutorial on writing NIFs. Note that the C code requires instrumentation, so hand-writing a NIF is likely not our best option.