Skip to content

runtime: configurable string field type support (groundwork for #127)#143

Merged
iainmcgin merged 4 commits into
mainfrom
feat/configurable-string-type
May 22, 2026
Merged

runtime: configurable string field type support (groundwork for #127)#143
iainmcgin merged 4 commits into
mainfrom
feat/configurable-string-type

Conversation

@iainmcgin

Copy link
Copy Markdown
Collaborator

Summary

Runtime groundwork for #127 — letting generated string fields use a small-string-optimized type instead of String. This PR adds the runtime substrate only; the user-facing buffa_build string_type knob and codegen branching land in a follow-up that builds on this.

Generated output is unchanged by this PR — nothing emits anything other than String yet.

What's here

  • ProtoString trait (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.
  • Generic decode/JSON paths: decode_string_to::<S>() alongside decode_string, and the proto_string serde with-module generalized (serialize over AsRef<str>, deserialize constructs the target type directly so an SSO type inlines short strings without an intermediate String). For the default String representation every conversion is the identity — zero added cost.
  • Three optional, feature-gated string cratessmol_str, ecow, compact_str — re-exported (#[doc(hidden)], mirroring the existing bytes re-export) so generated code can name ::buffa::smol_str::SmolStr without the consumer declaring the dep. Each ships a ProtoElemJson impl for repeated/map fields; ecow (no native Arbitrary) gets arbitrary_ecow* shims.

Feature plumbing keeps all three no_std (default-features = false) and turns on their serde/arbitrary/std sub-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 via Arc<str> and is purpose-built for read-mostly identifier-heavy data. compact_str is the most-downloaded option but trades that for mutability + O(n) clone, and ecow offers 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, and cargo 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, and arbitrary_ecow* parity against the underlying String impls.

iainmcgin added 3 commits May 22, 2026 16:39
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.
@github-actions

github-actions Bot commented May 22, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@iainmcgin iainmcgin marked this pull request as ready for review May 22, 2026 17:48
@iainmcgin iainmcgin requested review from kollektiv and rpb-ant May 22, 2026 17:48
@iainmcgin iainmcgin merged commit 807f241 into main May 22, 2026
7 checks passed
@iainmcgin iainmcgin deleted the feat/configurable-string-type branch May 22, 2026 18:22
@github-actions github-actions Bot locked and limited conversation to collaborators May 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants