runtime: configurable string field type support (groundwork for #127)#143
Merged
Conversation
Introduce a `ProtoString` trait (blanket-implemented for any type that is Clone + PartialEq + Default + Debug + AsRef<str> + From<String> + From<&str>) describing the surface generated code needs of a proto `string` field's Rust representation. `String` satisfies it, and so do small-string types like SmolStr / EcoString / CompactString. Add `decode_string_to::<S>()` as the generic counterpart to `decode_string` (decode + UTF-8 validate, then construct via `From<String>`), and make the `proto_string` serde with-module generic: `serialize` over `AsRef<str>` and `deserialize` over `From<String>`, mirroring the existing `bytes` module. For the default `String` representation every conversion is the identity, so the generic path is zero-cost and generated output is unchanged. This is groundwork for a configurable `string_type` codegen knob; no field is yet emitted as anything other than `String`.
Wire the three small-string crates into buffa as optional, feature-gated
dependencies and provide the runtime glue codegen will need:
- Re-export each crate (`::buffa::smol_str`, `::buffa::ecow`,
`::buffa::compact_str`) under its feature, mirroring the `bytes` re-export,
so generated code can name the type without the consumer declaring the dep.
- ProtoElemJson impls for repeated/map fields, delegating to the now-generic
`proto_string` with-module (one line each, no per-type shim).
- arbitrary_ecow{,_opt,_vec} helpers, since ecow ships no native Arbitrary;
smol_str and compact_str use their own impls via the feature plumbing.
Feature plumbing keeps the crates no_std (default-features = false) and turns
on their serde/arbitrary/std sub-features only when buffa's matching feature is
enabled, via weak (`dep?/feature`) references.
Nothing emits these types yet — that is the codegen knob in a follow-up.
Address review feedback on the string-type groundwork: - proto_string::deserialize now constructs the target type directly in the visitor (via From<&str> in visit_str), so an SSO type inlines a short JSON string without first allocating an intermediate String. The default String path is unchanged. - Add Send + Sync to the ProtoString bound so a message owning such a field stays Send + Sync; an exotic non-thread-safe string type is now rejected at the field type rather than silently infecting every containing message. - Add a compile-time assertion that String: ProtoString to freeze the default path against future supertrait changes. - Replace the broken intra-doc links to optional-dependency types with plain inline code so `cargo doc` succeeds under default features (the crate denies broken intra-doc links). - Document the three new feature flags in the crate-level table, including how they compose with json/arbitrary, and reword docs/comments so they describe the string_type knob as forthcoming rather than already shipped. - Widen the decode tests to straddle each SSO type's inline capacity.
|
All contributors have signed the CLA ✍️ ✅ |
rpb-ant
approved these changes
May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Runtime groundwork for #127 — letting generated
stringfields use a small-string-optimized type instead ofString. This PR adds the runtime substrate only; the user-facingbuffa_buildstring_typeknob and codegen branching land in a follow-up that builds on this.Generated output is unchanged by this PR — nothing emits anything other than
Stringyet.What's here
ProtoStringtrait (buffa::ProtoString) with a blanket impl, describing exactly what generated code needs of a string field's Rust type:Clone + PartialEq + Default + Debug + Send + Sync + AsRef<str> + From<String> + From<&str>. Nothing to implement by hand.decode_string_to::<S>()alongsidedecode_string, and theproto_stringserde with-module generalized (serializeoverAsRef<str>,deserializeconstructs the target type directly so an SSO type inlines short strings without an intermediateString). For the defaultStringrepresentation every conversion is the identity — zero added cost.smol_str,ecow,compact_str— re-exported (#[doc(hidden)], mirroring the existingbytesre-export) so generated code can name::buffa::smol_str::SmolStrwithout the consumer declaring the dep. Each ships aProtoElemJsonimpl for repeated/map fields;ecow(no nativeArbitrary) getsarbitrary_ecow*shims.Feature plumbing keeps all three
no_std(default-features = false) and turns on theirserde/arbitrary/stdsub-features only when buffa's matching feature is also enabled, via weak (dep?/feature) refs.Why these three
smol_str(the requested crate) gives O(1) clone of long strings viaArc<str>and is purpose-built for read-mostly identifier-heavy data.compact_stris the most-downloaded option but trades that for mutability + O(n) clone, andecowoffers a smaller 16-byte footprint — different points on the tradeoff curve, so the design exposes all three rather than picking one.Testing
cargo test,clippy -D warnings, andcargo doc(default and--all-features) all pass; verified across std/no_std and every relevant feature combination. New tests cover decode parity (inputs straddling each type's inline cap), JSON round-trip (singular + repeated) for all three types, andarbitrary_ecow*parity against the underlyingStringimpls.