) _____ _ _
/(( / __ \ | | (_)
(_))\ | / \/ __ _ _ _| |_ ___ _ __ _ _______
_)((_) | | / _` | | | | __/ _ \ '__| |_ / _ \
\ ^ / | \__/\ (_| | |_| | || __/ | | |/ / __/
\_/ \____/\__,_|\__,_|\__\___|_| |_/___\___|
This is an Erlang implementation of a Cauterize encoder/decoder. It differs from several of the other implementations in that it is implemented as a library that consumes an Erlang representation of the Cauterize specification. The code generation tool caut-erl-ref
generates an Erlang module that contains the Erlang representation of the specification as well as an encode/1
and a decode/2
function that call into the cauterize.erl
library.
Firstly, you will need to run make
. This will build both the Haskell code-generation tool and the Erlang library. Once make
suceeds, you will be able to generate Erlang modules from Cauterize specification files:
stack exec caut-erl-ref -- -s somefile.spec -o myproject/src
In the target directory, you will now have an Erlang module. The name of the module will reflect the Cauterize schema name, so it might require some editing or some single quotes to be callable from Erlang.
If you want to see a sample generation, you can use make generate
, which will generate a module in the test
directory.
Because the Erlang representation of Cauterize structures is self-describing, you do not need to pass the field name to the module when using encode/1
, but you MUST supply the correct field name when using decode/2
because Cauterize data itself does not contain that information. Typically a Cauterize schema will have a 'top level type' that holds the other types inside it, and this will be the type you pass to decode/2
.
The Erlang representation of Cauterize schemas is fashioned out of lists and tuples. It is symmetrical (you can pass the result of a decode to an encode and it will result in the same data), it is key-value-coded and it tries to be self-descriptive. Below are examples of each of the non-primitive Cauterize types in Erlang syntax. Everything is always wrapped in a top-level list and the outermost structure is always described by name, nested structures are not because they are unambigious.
Given a Cauterize Range like this, you can construct an instance of it in Erlang like this:
[{some_range, 1005}]
Given a Cauterize Array like this, you can construct an instance of it in Erlang like this:
[{mac, [1, 2, 3, 4, 5, 6, 7, 8]}]
Given a Cauterize Vector like this, you can construct an instance of it in Erlang like this:
[{byte_buffer_4k, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}]
Given a Cauterize Enumeration like this, you can construct an instance of it in Erlang like this:
{days_of_week, monday}]
Given a Cauterize Record like this, you can construct an instance of it in Erlang like this:
[{person, [{age, 33}, {height, 163}, {likes_cats, false}]}]
Given a Cauterize Union like this, you can construct an instance of it in Erlang like this:
[{request, [{set_key, [{name, "power"}, {value, 9001}]}]}]
Empty fields are considered to be 'true':
[{request, [{get_key, true}]}]
Unions have what seems like a superflous list wrapping the 2-tuple, but it needed to make KVC traversal work. This gets inconvienent for encoding though, so shorthand forms are supported:
[{request, {set_key, [{name, "power"}, {value, 9001}]}}]
[{request, {get_key, true}}]
[{request, get_key}]
Note that decode
will always return the KVC form, because it makes it much easier to traverse the structure of complicated specifications.
Given a Cauterize Combination like this, you can construct an instance of it in Erlang like this:
[{sensed, [{ambient_temp, 24}, {air_pressure, 5000}]}]
If you edit the generated Erlang module to change the primitive type of a vector or an array from u8
to char
, they will decode as binaries, not lists of u8s. This can sometimes be handy.
erl-caut-ref tries very hard to use key-value coded structures to make it easy to access the fields without cumbersome pattern matching and destructuring code. KVC can help here:
1> X = [{a_combination, [{a, 223372036854775808}, {b, -99}, {c, [{a,[{z, [10, 11, 12, 13]}, {a, -1}, {d, [{a, 1}, {d, [{a, 0}, {b, 1}]}]}]}] }]}].
[{a_combination,[{a,223372036854775808},
{b,-99},
{c,[{a,[{z,"\n\v\f\r"},
{a,-1},
{d,[{a,1},{d,[{a,0},{b,1}]}]}]}]}]}]
2> kvc:path([a_combination, a], X).
223372036854775808
3> kvc:path([a_combination, c, a], X).
[{z,"\n\v\f\r"},{a,-1},{d,[{a,1},{d,[{a,0},{b,1}]}]}]
4> kvc:path([a_combination, c, a, d
77FE
, d, b], X).
1
KVC is sort of like a lite version of XPATH or something.
To see some more elaborate encoding/decoding examples, take a look at test/cauterize_schema_test.erl
.
When an encode or a decode fails, you will get back an {error, Reason}
tuple. This will contain information about which field failed to encode/decode, why it failed and, for decode only, a stack of the elements decoded up to the point the failure occured. This can be helpful when you want to understand what a truncated Cauterize structure is, but given Cauterize's lack of self-description, this may be misleading if the beginning of the structure is corrupted or missing.