feat(buffa): add MessageFullName trait#108
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
To be extra clear, intentionally opt-out from |
0be12cc to
ad3c34e
Compare
|
@iainmcgin could help me on this one? right now I decided to manually maintain the string since the extra extension stuff add a lot of noise in the PR, I am hoping this would be the direction to avoid the interface segregation anti-pattern. Most likely, breaking changing the extension set I think |
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
|
Hi yes I've been tinkering with this one a bit, thanks for the contribution. It's a very busy week but I'll try and get something together in the next few days for this - likely as part of a 0.6.0 release. |
|
Aye, I am around to do the work, just let me know and I jump in |
…rait Reshape the trait before it ships and the surface is semver-frozen: - `MessageFullName` -> `MessageName`. Matches `prost::Name`, the ecosystem precedent for "compile-time access to a message's protobuf identifiers", and reads cleaner as a generic bound. - Add `const PACKAGE`, `NAME`, and `TYPE_URL` alongside `FULL_NAME`. `prost::Name` ships `PACKAGE` + `NAME` + runtime `full_name()` / `type_url()`; buffa already computes all four at codegen time, so ship them as `&'static str` literals -- zero runtime cost. `PACKAGE` and `NAME` matter independently because the dotted `FULL_NAME` cannot be split unambiguously (`foo.Bar.Baz` could be package `foo.Bar` + message `Baz` or package `foo` + nested `Bar.Baz`); codegen knows which. Adding required consts to a published trait is a breaking change, so land the full set now. The trait's `TYPE_URL` is the same string as the inherent `Foo::TYPE_URL` const codegen has emitted since 0.4.0 and is documented as such. - Drop the `Message` supertrait and implement the trait for view types too (`impl<'a> MessageName for FooView<'a>`). The use case the PR targets -- type-erased event registries, structured logging -- wants to read the FQN from a zero-copy view without round-tripping through `to_owned_message()`, and a name-keyed registry shouldn't have to prove it can encode just to register a type. The const doesn't reach into the wire codec, so nothing requires `Message`. Codegen extracts the impl emission into a shared `message_name_impl(package, fqn, generics, ty)` helper used by both `impl_message.rs` (owned) and `view.rs` (view). Tests cover the package / name split, the nested-message dotted `NAME`, the inherent-const parity, and the view-type impl. `docs/migration-from-prost.md` now documents the `prost::Name -> MessageName` migration; `docs/guide.md`'s hand-written-impl example covers all four consts. Regen'd `buffa-descriptor`, `buffa-types`, `examples/logging`.
|
[claude code] Thanks for the PR — the motivation (compile-time FQN access for type-erased dispatch without descriptor machinery) is a real gap and the trait shape is the right tool. I pushed two commits to the fork (
If any of this doesn't fit your use case, happy to discuss — the |
- `message_name_impl()`: strip the `"<package>."` prefix atomically. The
two-step `strip_prefix(package)` then `strip_prefix(".")` would
partial-match a prefix-overlapping package (`package = "foo"` against
`proto_fqn = "food.Bar"`), leave `NAME = "food.Bar"`, and silently
violate the documented `PACKAGE + "." + NAME == FULL_NAME` invariant.
An atomic `strip_prefix("foo.")` only matches the real prefix.
- `MessageName::NAME` doc: clarify that `NAME` is the dotted "type name
relative to the package" (matching `prost::Name::NAME`), *not*
`DescriptorProto.name` which is only the leaf segment for nested
types. Add a runnable doctest showing the `T: MessageName` bound
pattern.
- Codegen tests: `test_message_name_consts` pins all four consts for
packaged + nested + empty-package + nested-in-empty-package shapes,
including the `PACKAGE + "." + NAME == FULL_NAME` invariant the
atomic strip protects. `gated_view_module_is_cfg_blocked` now also
asserts the view's `impl MessageName` lives in the `View` content
file (gated behind `feature = "views"`) and the owned impl lives in
the `Owned` content file (unconditional).
user.UserCreated). To use abuffa-generated message directly as a domain event. Skipping the usual hand-written mapping layer between "domain type" and "storage type". Callers need that name as a compile-time constant on the type itself. Today it isn't there: the closest surface,ExtensionSet::PROTO_FQN, is bundled with the extension machinery and only emitted when a message opts intounknown_fields=true, which adds a hidden__buffa_unknown_fieldsfield to the struct and forces every struct-literal construction in domain code to trail..Default::default(). Reaching forbuffa::ExtensionSetjust to read the FQN therefore drags in storage and ergonomic costs the caller doesn't want.buffa::MessageFullNameas a standalone trait with a singleconst FULL_NAME: &'static stritem. Kept out ofMessageprecisely so the FQN concern stays orthogonal and adding it doesn't break any hand-writtenimpl Messagein downstream code. Generic call sites import the trait (use buffa::MessageFullName;) and readT::FULL_NAMEat compile time with no runtime indirection, so event stores, type registries, andAnytype-URL construction can dispatch on it instead of maintaining a hand-written match arm per message.ExtensionSet::PROTO_FQNis intentionally left in place to keep this change additive; the two consts are emitted from the sameproto_fqnsource in codegen so they cannot drift, and that invariant is documented at both sites.MessageFullName/FULL_NAMEmirrors the convention used across the protobuf ecosystem (and matches protocolbuffers/protobuf#27111 to keep the two PRs aligned). Not attached to it. Happy to rename if a different spelling reads better here.Related: TrogonStack/trogonai#108.