prototools
A collection of protobuf utilities written in Rust.
prototext
Lossless, bidirectional converter between binary protobuf wire format and human-readable text. Three promises:
- Lossless round-trip —
binary → text → binaryis byte-for-byte identical for any input: well-formed, non-canonical, or malformed. - protoc-compatible — for canonical protobuf messages the text output is
identical to
protoc --decode. - Schema-aware — supply a compiled
.pbdescriptor and a root message type to get field names, proto types, and enum values. Works without a schema too: every field is then decoded by wire type and field number.
Installation
NixOS / nix-shell
Build and install from the repo:
git clone https://github.com/douzebis/prototools
cd prototools
nix-build # result/bin/prototext
Man page and shell completions are installed automatically.
Or enter a development shell with prototext on PATH, completions and man
page activated for the current session:
nix-shell
cargo install
cargo install --locked prototext
--locked is required: it uses the dependency versions that were tested at
release time.
Man pages:
cargo installdoes not install man pages. To generate them locally, run:cargo install --locked --bin prototext-gen-man prototext prototext-gen-man ~/.local/share/man/man1
Shell Completions
When installed via nix-build, completion scripts are installed automatically.
When using nix-shell, bash completions are activated for the current session.
For a cargo install setup, add one line to your shell's startup file:
# bash (~/.bashrc)
# zsh (~/.zshrc)
# fish (~/.config/fish/config.fish)
PROTOTEXT_COMPLETE=fish prototext | source
Quick start
google.protobuf.* types are embedded — no descriptor file needed. Decode
any .pb descriptor that protoc produces:
$ protoc -o timestamp.pb google/protobuf/timestamp.proto
$ prototext -d -t google.protobuf.FileDescriptorSet timestamp.pb
#@ prototext: protoc
file { #@ repeated FileDescriptorProto = 1
name: "google/protobuf/timestamp.proto" #@ string = 1
package: "google.protobuf" #@ string = 2
message_type { #@ repeated DescriptorProto = 4
name: "Timestamp" #@ string = 1
field { #@ repeated FieldDescriptorProto = 2
name: "seconds" #@ string = 1
number: 1 #@ int32 = 3
label: LABEL_OPTIONAL #@ Label(1) = 4
type: TYPE_INT64 #@ Type(3) = 5
json_name: "seconds" #@ string = 10
}
...
}
}
Encode back to binary and verify the round-trip is byte-exact:
$ prototext -d -t google.protobuf.FileDescriptorSet timestamp.pb | \
prototext -e | diff - timestamp.pb && echo "byte-exact"
byte-exact
Non-canonical encoding — protobuf varints can carry redundant continuation
bytes and still decode to the same value. Standard tools discard these bytes;
prototext preserves them via inline annotations:
$ printf '\x08\xaa\x00' | prototext -d
#@ prototext: protoc
1: 42 #@ varint; val_ohb: 1
Field 1 = 42, but encoded in three bytes instead of the canonical two
(val_ohb: 1 records the one redundant byte). The round-trip is still
byte-exact:
$ printf '\x08\xaa\x00' | prototext -d | prototext -e | od -A n -t x1
08 aa 00
For full usage see man prototext or the
online docs.
License
MIT — see LICENSES/MIT.txt.