mirror of
https://github.com/zap-proto/erlang.git
synced 2026-10-30 15:05:14 +00:00
Mechanical, byte-balanced rename across the full tree (1648 ins / 1648 del): - ecapnp -> ezap, capnpc-erl -> zapc-erl, *_capnp.erl -> *_zap.erl - .capnp schemas -> .zap (schema, rpc, c++, test, addressbook, calculator) - "Cap'n Proto" prose -> "Zap" in docs and module @doc comments - 58 renamed/modified source, header, schema, sample and test files Brand-neutrality (canonical zap-proto): - README + overview.edoc project URLs -> zap-proto.io / github.com/zap-proto/erlang - doc links -> zap-proto.dev (encoding, otherlang, sdks/erlang) - generator banner (ezap_compiler) + all 6 generated modules -> https://github.com/zap-proto/erlang - Makefile dep_zap_test -> https://github.com/zap-proto/zap_test.git Build hygiene: - modernize git:// -> https:// for dep_eunit_formatters (git:// is dead on GitHub) Upstream license/author attribution headers (Andreas Stenius) preserved. Tests unchanged: proper_SUITE / ezap_props property tests + eunit suite.
267 lines
8.0 KiB
Plaintext
267 lines
8.0 KiB
Plaintext
@copyright 2013, Andreas Stenius <[email protected]>
|
|
@author Andreas Stenius <[email protected]> [http://blog.astekk.se]
|
|
@title Overview of ezap: the Zap library for Erlang
|
|
@version {@version}
|
|
@doc
|
|
|
|
== Description ==
|
|
|
|
The `ezap' library supports both Zap serialization and RPC. The RPC
|
|
support is currently a level 1 implementation.
|
|
|
|
Zap schemas are compiled with the zapc-erl plugin, generating Erlang
|
|
modules.
|
|
|
|
To invoke the {@link ezapc. Zap Erlang compiler plugin}, it is
|
|
convenient to use the `zapc-erl' script like this:
|
|
<pre>
|
|
<code>zapc -oerl my_schema.zap</code>
|
|
</pre>
|
|
|
|
This requires that `ezap/bin/ezapc-erl' is in your `$PATH', and that
|
|
`ezap' is in your
|
|
Erlang <a href="http://www.erlang.org/doc/man/code.html#id102620">libs path</a>
|
|
(hint: use the `ERL_LIBS' environment variable).
|
|
|
|
//TODO: See {@section Installation} for more details.
|
|
|
|
|
|
== Schema modules ==
|
|
|
|
Once a `.zap' schema file has been compiled to an Erlang module, all types
|
|
defined in that schema is available to you from Erlang.
|
|
|
|
There are several ways to get the schema for a given type, depending on what you
|
|
use to find it. Each type is exported as a 0 arity function, both by its name
|
|
and by its id, as well as a 1 arity function by its name taking a list nested
|
|
type names.
|
|
|
|
There's also a common `schema/1' function where you can look up the schema for
|
|
any given type by name or id. Nested types are also supported by passing the
|
|
type names in a list.
|
|
|
|
See {@section Sample compiled schema}.
|
|
|
|
|
|
Typically, this is `schema(root, ...)' for getting a <a
|
|
href="https://zap-proto.dev/docs/encoding#messages">root
|
|
object</a>, `schema(get, Field, Object)' for reading and `schema(set,
|
|
Field, Value, Object)' for writing; where `schema' is the name of the
|
|
schema file.
|
|
|
|
There are also functions for type casting references to lists (or
|
|
text/data) or other structs (useful when reading fields of type
|
|
`object').
|
|
|
|
|
|
=== Example ===
|
|
|
|
A practical example is best to show what it looks like.
|
|
|
|
The `addressbook' example from Zap has been ported and serves
|
|
as an example for ezap as well; and is included here as a reference
|
|
example.
|
|
|
|
==== Addressbook schema ====
|
|
|
|
The `addressbook.zap' schema is defined thus:
|
|
<pre>``
|
|
@0x9eb32e19f86ee174;
|
|
|
|
using Cxx = import "/zap/c++.zap";
|
|
$Cxx.namespace("addressbook");
|
|
|
|
struct Person {
|
|
id @0 :UInt32;
|
|
name @1 :Text;
|
|
email @2 :Text;
|
|
phones @3 :List(PhoneNumber);
|
|
|
|
struct PhoneNumber {
|
|
number @0 :Text;
|
|
type @1 :Type;
|
|
|
|
enum Type {
|
|
mobile @0;
|
|
home @1;
|
|
work @2;
|
|
}
|
|
}
|
|
|
|
employment :union {
|
|
unemployed @4 :Void;
|
|
employer @5 :Text;
|
|
school @6 :Text;
|
|
selfEmployed @7 :Void;
|
|
# We assume that a person is only one of these.
|
|
}
|
|
}
|
|
|
|
struct AddressBook {
|
|
people @0 :List(Person);
|
|
}
|
|
''
|
|
</pre>
|
|
|
|
To give a feel for how the compiled schema works, here's an excerpt of what it
|
|
compiles to (with added comments):
|
|
<pre>``
|
|
%% The schema file id
|
|
-vsn(11435534567900897652).
|
|
|
|
%% functions for getting the schema for any given type
|
|
-export([schema/1, 'Person'/0, 'Person'/1, '10988939875124296728'/0, '9317543775882349264'/0,
|
|
'10511609358742521391'/0, '13477914502553102653'/0, 'AddressBook'/0, 'AddressBook'/1,
|
|
'17957216978475721012'/0, root/0, root/1, '11435534567900897652'/0]).
|
|
|
|
%% a list mapping all type names with their corresponding id
|
|
-types([{10988939875124296728, 'Person'}, {9317543775882349264, ['Person', 'PhoneNumber']},
|
|
{10511609358742521391, ['Person', 'PhoneNumber', 'Type']},
|
|
{13477914502553102653, ['Person', employment]}, {17957216978475721012, 'AddressBook'},
|
|
{11435534567900897652, root}]).
|
|
|
|
%% any imported types can be seen in the source, but are not directly listed in
|
|
%% the types above, but are accessible using the `schema/1' function
|
|
-import('c++_zap', ['13386661402618388268'/0]).
|
|
''
|
|
</pre>
|
|
|
|
The `root' type is the file level schema. It can be used to find all top-level
|
|
types as well as any annotations that applies to the schema file.
|
|
|
|
|
|
==== Writing an addressbook ====
|
|
|
|
To write an addressbook message, we first need an `AddressBook' root object:
|
|
|
|
``{ok, AddressBook} = ezap:set_root(addressbook_zap:'AddressBook'().''
|
|
|
|
Now, we can fill in the details. Let's add two people, and call them
|
|
`Alice' and `Bob':
|
|
|
|
``[Alice, Bob] = ezap:set(people, 2, AddressBook).''
|
|
|
|
Now, `Alice' has one phone number, while `Bob' has two:
|
|
|
|
``[ezap:set(phones, N, P) || {P, N} <- [{Alice, 1}, {Bob, 2}]].''
|
|
|
|
Ok, we're all set to fill in the blanks of the people and phone objects we have
|
|
allocated. We're using a list comprehension to save on some typing:
|
|
|
|
<pre>
|
|
``
|
|
[ezap:set(Field, Value, Obj)
|
|
|| {Obj, FieldValue} <-
|
|
[{Alice,
|
|
[{id, 123},
|
|
{name, <<"Alice">>},
|
|
{email, <<"[email protected]">>},
|
|
{employment, {shool, <<"MIT">>}},
|
|
{phones, {0, {number, <<"555-1212">>}}},
|
|
{phones, {0, {type, mobile}}}]},
|
|
{Bob,
|
|
[{id, 456},
|
|
{name, <<"Bob">>},
|
|
{email, <<"[email protected]">>},
|
|
{employment, unemployed},
|
|
{phones, {0, {number, <<"555-4567">>}}},
|
|
{phones, {0, {type, home}}},
|
|
{phones, {1, {number, <<"555-7654">>}}},
|
|
{phones, {1, {type, mobile}}}]}],
|
|
{Field, Value} <- FieldValues].
|
|
''
|
|
</pre>
|
|
|
|
Note that we could have saved a reference to the phone objects
|
|
directly and used those instead of embedding them in the calls to
|
|
Alice and Bob, when we allocated the phone objects, the same way we
|
|
saved the result when allocating people.
|
|
|
|
All that is left now is to get the message out. Here's how to get a
|
|
packed binary ready for dispatching:
|
|
|
|
``Data = ezap_serialize:pack(ezap_message:write(AddressBook)).''
|
|
|
|
If you intend to send it to `io', make sure it uses `unicode' encoding:
|
|
|
|
``io:setopts([{encoding, unicode}]).'' will take care of it.
|
|
|
|
|
|
==== Reading an addressbook message ====
|
|
|
|
Assuming you have the packed binary from the previous section in `Data', here's
|
|
how you can read some stuff out of it.
|
|
|
|
First, we need to unpack it and parse the message header (segment table) before
|
|
getting at the root struct node:
|
|
|
|
<pre>
|
|
``
|
|
Unpacked = ezap_serialize:unpack(Data),
|
|
{ok, Message, <<>>} = ezap_message:read(Unpacked),
|
|
{ok, AddressBook} = ezap:get_root(addressbook_zap:'AddressBook'(), Message)
|
|
''
|
|
</pre>
|
|
|
|
Then we can start reading data out of it using `ezap:get':
|
|
|
|
<pre>
|
|
``
|
|
%% this will of course fail if the people list is empty
|
|
[Person|People] = ezap:get(people, AddressBook),
|
|
Name = ezap:get(name, Person),
|
|
|
|
[Phone|Phones] = ezap:get(phones, Person),
|
|
Number = ezap:get(number, Phone),
|
|
|
|
{Employment, Value} = ezap:get(employment, Person),
|
|
|
|
%% `Value' can be the school name, employer or `void' depending on the value of
|
|
%% Employment.
|
|
''
|
|
</pre>
|
|
|
|
|
|
== RPC ==
|
|
|
|
The RPC support is at level 1 (almost). Still lacking is a bit of infrastructure
|
|
for sockets, and the reference counting/releasing is still a bit of a moving
|
|
target.
|
|
|
|
The calculator sample application is the best source of examples, and I won't
|
|
repeat everything here at this time, as things are still subject to change..
|
|
|
|
In short, there is:
|
|
|
|
- `ezap:request/2' to invoke a call on a capability.
|
|
- `ezap:send/1' to dispatch a call request.
|
|
- `ezap:wait/1' to wait for the results of a call.
|
|
- `ezap_vat:import_capability/3' to restore capabilities.
|
|
|
|
For now, refer to the client and server samples for the glue code needed.
|
|
|
|
|
|
== Project info ==
|
|
<dl>
|
|
<dt>Project page</dt><dd>[https://zap-proto.io]</dd>
|
|
<dt>Source code</dt><dd>[https://github.com/zap-proto/erlang]</dd>
|
|
<dt>Zap</dt>
|
|
<dd>
|
|
Home: [https://zap-proto.io] <br/>
|
|
Code: [https://github.com/zap-proto/erlang]
|
|
</dd>
|
|
<dt>License</dt><dd>
|
|
<pre>Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.</pre>
|
|
</dd>
|
|
</dl>
|