Apologies, this question has been asked in numerous variations before, but it seems I haven’t found the answer I’m looking for.
I’ve found myself in a situation where I need to support multiple versions of protobuf messages. This was due to some poor decisions by the vendor in changing protobuf files for different versions of software they’re running. i.e. they’ve kept the same protobuf files between different software versions but changed some data types on existing fields in messages. I need to be able to interface with both versions of their software…
So I need to generate both sets of protobuf files but with different namespaces
I did start doing this (which works) but it’s very tedious:
defmodule MyModule do
use Protox, files: ["./proto/v1/foo.proto"], namespace: V1
use Protox, files: ["./proto/v2/foo.proto"], namespace: V2
...
use Protox, files: ["./proto/vN/foo.proto"], namespace: VN
end
I’ve attempted this (does not work):
defmodule MyModule do
MyOtherModule.get_files_and_namespaces()
|> Enum.each(fn {files, namespace} ->
use Protox, files: files, namespace: namespace
end)
end
This responds with the usual:
variable "files" does not exist and is being expanded to "files()", please use parentheses to remove the ambiguity or change the variable name
I’ve attempted to use unquote/1
defmodule MyModule do
MyOtherModule.get_files_and_namespaces()
|> Enum.each(fn {files, namespace} ->
use Protox, unquote(files: files, namespace: namespace)
end)
end
This responds with:
== Compilation error in file lib/mymodule.ex ==
** (CompileError) nofile:4: unquote called outside quote
(elixir 1.12.3) lib/code.ex:1036: Code.eval_quoted/3
(protox 1.7.0) expanding macro: Protox.__using__/1
lib/mymodule.ex:4: MyModule (module)
(elixir 1.12.3) expanding macro: Kernel.use/2
lib/mymodule.ex:4: MyModule (module)
(elixir 1.12.3) expanding macro: Kernel.|>/2
The __using__/1
macro from the Protox module looks like this:
defmacro __using__(opts) do
{opts, _} = Code.eval_quoted(opts)
{paths, opts} = get_paths(opts)
{files, opts} = get_files(opts)
{:ok, file_descriptor_set} = Protox.Protoc.run(files, paths)
%{enums: enums, messages: messages} = Protox.Parse.parse(file_descriptor_set, opts)
quote do
unquote(make_external_resources(files))
unquote(Protox.Define.define(enums, messages, opts))
end
end
Can anyone relieve me from my macro ignorance?
Thank-you in advance