You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Repo:Automattic/wordpress-rs (Rust + UniFFI → Swift & Kotlin bindings). Not GutenbergKit. Tracking: GutenbergKit#579. Prior art: wordpress-rs#1366 (the rest_route discovery fix, shipped in 0.6.0).
Why
GutenbergKit#579 consolidates six hand-rolled rest_route URL joiners onto wprs's WpOrgSiteApiUrlResolver. The resolver already does the rest_route-aware path join and is exported. The one gap: consumers can't attach endpoint query parameters (context=edit, status=active, exclude=core,gutenberg) to a resolved URL without re-implementing the ?→& merge — because resolve() takes no query and ParsedUrl's query methods aren't exported across FFI. Closing this gap unblocks GutenbergKit adoption and deletes the Swift + Kotlin copies of the merge.
Goal
Expose, across the UniFFI boundary (Swift + Kotlin), a rest_route-aware way to attach arbitrary query pairs to a resolved REST URL. Additive, non-breaking.
wp_api/src/url_query.rs — existing pub(crate)AppendUrlQueryPairs trait + QueryPairs wrapper (the internal typed-request query mechanism). Reuse this machinery for consistency where practical.
Recommended approach
Add an exported method on ParsedUrl (a general, reusable URL primitive — composes with resolve()):
#[uniffi::export]implParsedUrl{/// Appends query parameters to this URL, preserving any existing query./// Works uniformly on path roots and query-based (`?rest_route=`) roots:/// `append_pair` adds `&k=v` after the existing query (the rest_route value)/// or `?k=v` if there is none.pubfnby_appending_query_pairs(&self,pairs:Vec<QueryPair>) -> Arc<ParsedUrl>{letmut url = self.inner.clone();for p in&pairs {
url.query_pairs_mut().append_pair(&p.name,&p.value);}Arc::new(ParsedUrl::new(url))}}
Consumer flow (host or GutenbergKit): resolver.resolve(ns, segments).byAppendingQueryPairs([...]). Correct on both root forms because append_pair handles the ?/& bookkeeping and encoding — the same mechanism by_extending_rest_api_path already uses, so behavior stays consistent.
Keep resolve(), route_path, and the ApiUrlResolver trait unchanged (the trait is with_foreign; adding a required method breaks Swift/Kotlin implementors).
Decisions to make (flagged, with evidence)
Param type across FFI. No #[uniffi::export] signature in the repo takes Vec<(String, String)> — tuples appear only in internal code — and the convention is #[derive(uniffi::Record)]. So introduce a small record:
First check url_query.rs for an existing Record-friendly pair type to reuse. If the pinned UniFFI version supports exported tuples and the team prefers them, Vec<(String, String)> is acceptable — confirm before choosing.
Optional resolver convenience. Optionally add resolve_with_query(namespace, segments, pairs) so callers resolve-and-attach in one call. Not required; the ParsedUrl method is the must-have. If added, keep it additive (new method, not a changed signature).
Behavior spec — golden cases (must hold)
Appending [{context, edit}, {status, active}] to a resolved base:
Reserved chars in a value, e.g. exclude=core,gutenberg → serialized exclude=core%2Cgutenberg (form-urlencoded; WordPress decodes it). Assert the encoded form deliberately — it's byte-different from GutenbergKit's current literal-comma output but functionally equivalent, and it matches by_extending_rest_api_path's existing encoding.
Order preserved; duplicate keys allowed (append, do not dedupe).
Tests
Add an rstest table in parsed_url.rs's test module, mirroring the existing #[case::rest_route_query_form(...)] style, covering every golden case above.
If the repo has a Swift/Kotlin binding smoke-test harness, add one call through the generated bindings to prove the FFI export + the QueryPair record round-trip.
Bindings / build verification
This must cross the UniFFI boundary. Regenerate and verify the Swift and Kotlin bindings expose by_appending_query_pairs and QueryPair.
Per the repo's own CHANGELOG note, make xcframework-only-macos is the fast way to verify a UniFFI change (not the full 11-target make xcframework). Confirm the Kotlin bindings regenerate cleanly too.
CHANGELOG
Add an ### Added entry under ## [Unreleased] in CHANGELOG.md, e.g.:
REST URL resolution can now attach endpoint query parameters via ParsedUrl.by_appending_query_pairs (Swift/Kotlin), so consumers building ?rest_route= URLs no longer re-implement the ?→& merge. Complements WpOrgSiteApiUrlResolver.resolve.
Repo:
Automattic/wordpress-rs(Rust + UniFFI → Swift & Kotlin bindings). Not GutenbergKit.Tracking: GutenbergKit#579. Prior art: wordpress-rs#1366 (the
rest_routediscovery fix, shipped in 0.6.0).Why
GutenbergKit#579 consolidates six hand-rolled
rest_routeURL joiners onto wprs'sWpOrgSiteApiUrlResolver. The resolver already does the rest_route-aware path join and is exported. The one gap: consumers can't attach endpoint query parameters (context=edit,status=active,exclude=core,gutenberg) to a resolved URL without re-implementing the?→&merge — becauseresolve()takes no query andParsedUrl's query methods aren't exported across FFI. Closing this gap unblocks GutenbergKit adoption and deletes the Swift + Kotlin copies of the merge.Goal
Expose, across the UniFFI boundary (Swift + Kotlin), a rest_route-aware way to attach arbitrary query pairs to a resolved REST URL. Additive, non-breaking.
Where the code is
wp_api/src/parsed_url.rs—ParsedUrl { inner: Url }. The rest_route joinby_extending_rest_api_path(non-exportedimpl, ~L46; note it usesquery_pairs_mut().append_pair, which percent-encodes). Exported block#[uniffi::export] impl ParsedUrl(~L127:parse/url/pretty_url).wp_api/src/request/endpoint.rs—ApiUrlResolvertrait (#[uniffi::export(with_foreign)], ~L136:resolve(namespace, segments),route_path(namespace, path));WpOrgSiteApiUrlResolver(~L147);resolvedelegates toby_extending_rest_api_path.wp_api/src/url_query.rs— existingpub(crate)AppendUrlQueryPairstrait +QueryPairswrapper (the internal typed-request query mechanism). Reuse this machinery for consistency where practical.Recommended approach
Add an exported method on
ParsedUrl(a general, reusable URL primitive — composes withresolve()):Consumer flow (host or GutenbergKit):
resolver.resolve(ns, segments).byAppendingQueryPairs([...]). Correct on both root forms becauseappend_pairhandles the?/&bookkeeping and encoding — the same mechanismby_extending_rest_api_pathalready uses, so behavior stays consistent.Keep
resolve(),route_path, and theApiUrlResolvertrait unchanged (the trait iswith_foreign; adding a required method breaks Swift/Kotlin implementors).Decisions to make (flagged, with evidence)
#[uniffi::export]signature in the repo takesVec<(String, String)>— tuples appear only in internal code — and the convention is#[derive(uniffi::Record)]. So introduce a small record:url_query.rsfor an existing Record-friendly pair type to reuse. If the pinned UniFFI version supports exported tuples and the team prefers them,Vec<(String, String)>is acceptable — confirm before choosing.resolve_with_query(namespace, segments, pairs)so callers resolve-and-attach in one call. Not required; theParsedUrlmethod is the must-have. If added, keep it additive (new method, not a changed signature).Behavior spec — golden cases (must hold)
Appending
[{context, edit}, {status, active}]to a resolved base:resolve)https://example.com/wp-json/wp/v2/themes(path root)…/wp/v2/themes?context=edit&status=activehttps://example.com/index.php?rest_route=%2Fwp%2Fv2%2Fthemes(query root)…?rest_route=%2Fwp%2Fv2%2Fthemes&context=edit&status=active&debug=1&debug=1preservedPlus:
pairs→ URL returned unchanged.exclude=core,gutenberg→ serializedexclude=core%2Cgutenberg(form-urlencoded; WordPress decodes it). Assert the encoded form deliberately — it's byte-different from GutenbergKit's current literal-comma output but functionally equivalent, and it matchesby_extending_rest_api_path's existing encoding.Tests
parsed_url.rs's test module, mirroring the existing#[case::rest_route_query_form(...)]style, covering every golden case above.QueryPairrecord round-trip.Bindings / build verification
by_appending_query_pairsandQueryPair.make xcframework-only-macosis the fast way to verify a UniFFI change (not the full 11-targetmake xcframework). Confirm the Kotlin bindings regenerate cleanly too.CHANGELOG
Add an
### Addedentry under## [Unreleased]inCHANGELOG.md, e.g.:Out of scope
EditorConfigurationAPI change (that's Bump serde from 1.0.217 to 1.0.218 #579 downstream).resolve()/route_pathsignatures or theApiUrlResolvertrait shape.Acceptance criteria
ParsedUrl.by_appending_query_pairs(+QueryPairif used) exported and callable from Swift and Kotlin.?rest_route=roots (trailing slash and none), preserves existing query params, order-stable, empty-safe, encoding matchesby_extending_rest_api_path.resolve()/route_path/ trait unchanged; change is purely additive.## [Unreleased] → AddedCHANGELOG entry.make xcframework-only-macos(or equivalent) confirms the Swift surface; Kotlin bindings regenerate cleanly.References
parsed_url.rs:wordpress-rs/wp_api/src/parsed_url.rs
Lines 46 to 101 in 835be43
endpoint.rs(resolver):wordpress-rs/wp_api/src/request/endpoint.rs
Lines 136 to 172 in 835be43