For folks looking to do something similar in the future, my suggestion here would be to dump some representation (ideally a json schema) of the salesforce objects into a file, and then derive your structs from that at compile time. Then, rebuild and redeploy as necessary. If you actually need these structs, of course
I agree with @D4no0 and @sodapopcan that in this case you’re not getting any benefit from dynamic runtime struct definition, in exchange for “weirdness” later down the line.
If what you’re looking for is something syntactic to signal that you’re operating on a salesforce object of X type, or to pattern match on that in some interesting or novel way, consider a macro. To be clear, I’m not suggesting that you do this necessarily, just showing that you don’t need structs to get customized pattern matching. Elixir has effectively all the facilities you need to modify the language.
defmodule SObject do
defstruct [:type, data: %{}]
defmacro so({:%, _, [type, {:%{}, meta, keys]}) do
string_keys =
Enum.map(keys, fn {key, value} ->
{to_string(key), value}
end)
contents_with_string_keys = {:%{}, meta, keys}
quote do
%SObject{type: unquote(to_string(type)), data: unquote(contents_with_string_keys)}
end
end
end
Then you can do this kind of thing
defmodule SomeModule do
import SObject
def do_something(so(%Case{number: case_number})) do
case_number
end
end
That all works without ever having to turn salesforce data from strings to atoms. You could then build in a known set of fields that exist for certain builtin objects (or don’t exist) and validate that at compile time with your macro.
Whether you ultimately want to use something like this is 100% up to you, but the main point I want to get across is that, with macros, you can often approach syntax that you like, without having to sacrifice how the underlying system works.