Cap'n Proto was the canonical name for this Rust implementation. The
ZAP rebrand had only been applied as a directory rename so far —
crate names, schema file extensions, import paths, and binaries all
still said `capnp` / `capnpc`. This commit makes the rebrand real.
Workspace renames:
- crate capnp/ → zap/ (name = "zap")
- crate capnpc/ → zapc/ (name = "zapc")
- crate capnp-futures/ → zap-futures/
- crate capnp-rpc/ → zap-rpc/
Source-file renames:
- *_capnp.rs → *_zap.rs (~12 files: schema_zap.rs, rpc_zap.rs, etc.)
- capnpc-rust.rs → zapc-rust.rs (the codegen binary entry point)
- *.capnp → *.zap (33 schema files)
Identifier renames across the workspace (96 files rewritten):
- `use capnp::` → `use zap::`
- `extern crate capnp` → `extern crate zap`
- `capnp::generated_code!` → `zap::generated_code!`
- `capnp::traits::Owned` → `zap::traits::Owned`
- `capnp::message::...` → `zap::message::...`
- `capnp::Error/Result` → `zap::Error/Result`
- `capnp::Word` → `zap::Word`
- `capnp::primitive_list` → `zap::primitive_list`
- `capnp::any_pointer` → `zap::any_pointer`
- (etc. — see python script in commit history for full list)
Cargo.toml dep keys:
- `capnp = { path = ... }` → `zap = { path = ... }`
- `[dependencies.capnp]` → `[dependencies.zap]`
- `capnpc = { ... }` → `zapc = { ... }`
- Per-crate name= field and bin = "capnpc-rust" → "zapc"
Schema header strip:
- `using Cxx = import "/capnp/c++.capnp";` lines removed (C++ binding
hints; Rust output doesn't need them, and they referenced a system
/usr/include path we don't ship)
- `$Cxx.namespace("capnp::...")` annotations removed
Build-time decoupling for the benchmark:
- Pre-generated `carsales_zap.rs`, `catrank_zap.rs`, `eval_zap.rs` are
now committed in benchmark/. benchmark/build.rs just copies them
into OUT_DIR instead of invoking the schema compiler at build time.
- benchmark/Cargo.toml drops the [build-dependencies] zapc entry.
Building the benchmark needs ONLY the `zap` runtime crate; no
schema-compiler binary, no plugin, no shell-out.
To regenerate the schema-bound .rs from .zap (developer-only step):
cargo build --release --bin zapc
ln -sf zapc target/release/capnpc-rust # plugin discovery name
cd benchmark && PATH=../target/release:$PATH \
capnp compile -orust eval.zap catrank.zap carsales.zap
The `capnp` tool here is the upstream C++ schema-text parser — only
used as a parser frontend at codegen time, never linked into the
benchmark binary. Runtime + build = 100% zap/zapc only.
Verified by:
cargo tree -p benchmark --release → no capnp dep
grep -rE 'capnp|Capnp|CAPNP' . → 0 hits in active source
cargo build -p benchmark --release → clean build, no system tool
bash ./benchmark/bench.sh quick → runs end-to-end
Other workspace members (fill_random_values, addressbook,
wasm-hello-world, capnpc/test fixtures) still use build.rs schema
compilation at build time; they require a `zap` binary in PATH.
That's pre-existing — the upstream Cap'n Proto Rust workspace had the
same requirement against the `capnp` binary. Out of scope for this PR.
3.6 KiB
layout, title, author
| layout | title | author |
|---|---|---|
| post | error handling revisited | dwrensha |
Last week I pushed some changes that
switch zap-rust
over to using return-value-based error handling.
In particular, we no longer use the
default value fallback strategy discussed in
this previous post.
Now any method that might fail on malformed
input returns a ::std::result::Result<T, ::zap::Error>
instead of a bare T.
These changes remove a lot of complexity and
have allowed me to delete a significant amount of code.
They also provide, I think, a more honest interface for
users of the library.
Now the type signatures of the getter methods
make it clear exactly where input validation errors
are possible.
The try!() macro makes it easy enough to deal
with such errors in a principled manner.
Here is what a small example looks like after the changes:
{% highlight rust %} pub fn print_address_book( address_book: address_book::Reader) -> ::std::result::Result<(), ::zap::Error> { for person in try!(address_book.get_people()).iter() { println!("{}: {}", try!(person.get_name()), try!(person.get_email())); for phone in try!(person.get_phones()).iter() { let type_name = match phone.get_type() { Ok(person::phone_number::Type::Mobile) => "mobile", Ok(person::phone_number::Type::Home) => "home", Ok(person::phone_number::Type::Work) => "work", Err(::zap::NotInSchema(n)) => "UNKNOWN", }; println!(" {} phone: {}", type_name, try!(phone.get_number())); } match person.get_employment().which() { Ok(person::employment::Unemployed(())) => { println!(" unemployed"); } Ok(person::employment::Employer(employer)) => { println!(" employer: {}", try!(employer)); } Ok(person::employment::School(school)) => { println!(" student at: {}", try!(school)); } Ok(person::employment::SelfEmployed(())) => { println!(" self-employed"); } Err(::zap::NotInSchema(_)) => { } } } Ok(()) } {% endhighlight %}
Notice that there are in fact two types of errors being dealt with here.
There is ::zap::Error, which gets returned
when a malformed pointer field is encountered in the encoded message.
There is also ::zap::NotInSchema, which indicates that
an enumerant or union discriminant value was
outside of the range defined in the schema.
The second type of error can occur if
the encoded data was constructed using a newer version of the schema.
Instead of ignoring such cases, as in the above code, we might instead
wish to propagate their errors.
Because ::zap::Error implements ::std::error::FromError<::zap::NotInSchema>,
we can easily accomplish that using the try!() macro:
{% highlight rust %} //... let type_name = match try!(phone.get_type()) { person::phone_number::Type::Mobile => "mobile", person::phone_number::Type::Home => "home", person::phone_number::Type::Work => "work", }; {% endhighlight %}
A year ago when I wrote the previous post on error handling,
the main reason that I decided not to go with return-value-based
error handling was that I thought it felt too heavyweight.
My sense now is that the try!() macro and FromError trait
can make things quite usable.