Skip to content

update versioned types#151

Merged
pgherveou merged 6 commits into
mainfrom
pg/fix-versioned-rust-types
Jun 1, 2026
Merged

update versioned types#151
pgherveou merged 6 commits into
mainfrom
pg/fix-versioned-rust-types

Conversation

@pgherveou
Copy link
Copy Markdown
Collaborator

@pgherveou pgherveou commented May 29, 2026

Versioned message envelopes

Versioned request/response/error types in truapi::versioned are SCALE enums
whose variants (V1, V2, …) are successive versions of one logical message,
newest last. They are generated by the versioned_type! function-like proc
macro:

versioned_type! {
    pub enum HostFooRequest  { V1 => v01::HostFooRequest }
    pub enum HostFooResponse { V1 }
}

The macro assigns positional SCALE codec indices, enforces contiguous
V1..=Vn, and implements Versioned (the Latest payload type, the LATEST
version number, and version()). Single-version envelopes additionally get
trivial IntoLatest/FromLatest; multi-version envelopes implement those by
hand, since the conversion between versions is bespoke. Adding a version is one
line on the envelope plus the hand-written conversion for the direction that
changed.

Conversion contract

Three traits express how a server moves between wire versions and its internal
"latest" representation. Both directions are total by construction, so the
mapping for an old client is decided at design time rather than failing at
runtime:

pub trait Versioned: Sized {
    type Latest;
    const LATEST: u8;
    fn version(&self) -> u8;
}

/// Request path: upgrade a received envelope to its latest payload.
pub trait IntoLatest: Versioned {
    fn into_latest(self) -> Self::Latest;
}

/// Response/error path: downgrade a latest payload to the variant `target` understands.
pub trait FromLatest: Versioned {
    fn from_latest(latest: Self::Latest, target: u8) -> Self;
}

Intended server usage

The traits are shaped so the whole version dance collapses into one generic
dispatch step. Handlers are written once against *::Latest and never observe
older versions. (The adapter itself is not part of this PR; it lands with the
server work.)

pub async fn dispatch<Req, Resp, Err, F, Fut>(req: Req, handle: F) -> Result<Resp, Err>
where
    Req: IntoLatest,
    Resp: FromLatest,
    Err: FromLatest,
    F: FnOnce(Req::Latest) -> Fut,
    Fut: Future<Output = Result<Resp::Latest, Err::Latest>>,
{
    let target = req.version();                  // version the caller spoke
    match handle(req.into_latest()).await {      // upgrade, then handle in latest terms
        Ok(r)  => Ok(Resp::from_latest(r, target)),   // downgrade response to caller's version
        Err(e) => Err(Err::from_latest(e, target)),   // errors downgrade the same way
    }
}

Codegen

Codegen treats only truapi::api::* traits as protocol surface and skips the
versioned helper traits, so the generated TypeScript client and wire tables
are byte-identical. The emitted types.ts carries no standalone Version
type, since the protocol exposes no such type.

pgherveou added 3 commits May 29, 2026 16:36
Generate versioned message envelopes with a function-like `versioned_type!`
proc macro that accepts an N-variant list, assigns positional SCALE codec
indices, and implements `Versioned` (Latest/LATEST/version). Single-version
envelopes also get trivial `IntoLatest`/`FromLatest`; multi-version envelopes
leave those bespoke conversions to hand-written impls.

The three traits in `truapi::versioned` define a total upgrade/downgrade
contract for a server to normalize requests to the latest version, handle
them in latest-only terms, and map results back to the caller's version.

Generated TypeScript output is byte-identical; codegen skips the helper traits.
@pgherveou pgherveou marked this pull request as ready for review May 29, 2026 20:37
@pgherveou pgherveou requested review from a team May 29, 2026 20:37
@pgherveou pgherveou force-pushed the pg/fix-versioned-rust-types branch from 461abc8 to b8ad4a1 Compare May 29, 2026 20:49
…ndices

- compute the contiguity check in usize so a >255-variant enum reports a clean error instead of overflowing the u8 counter

- accept an optional visibility on each declared enum (pub/pub(crate)/none) instead of requiring pub

- document that versioned_type! expands to crate::versioned::* paths and is meant for use inside the truapi crate

- add a multi-version regression test pinning positional codec indices (V1->0, V2->1), version(), and LATEST

- call versioned_type! via its fully-qualified path in the versioned modules

- refresh the local-e2e troubleshooting note that referenced the removed IntoVersion/Version API
@pgherveou pgherveou force-pushed the pg/fix-versioned-rust-types branch from b8ad4a1 to bb5b018 Compare May 29, 2026 21:08
@pgherveou pgherveou mentioned this pull request May 30, 2026
2 tasks
Copy link
Copy Markdown
Collaborator

@filvecchiato filvecchiato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean design. Total conversions by construction (IntoLatest/FromLatest are infallible) is the right property — version mismatches are decided at design time, not runtime.

The proc macro is well-structured: contiguity validation, proper error spans, single-variant auto-impl vs multi-variant manual. Test coverage looks good.

Will conflict with #145 on versioned/mod.rs (both remove the old Version/IntoVersion), but resolution is straightforward.

Copy link
Copy Markdown
Collaborator

@filvecchiato filvecchiato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good

@pgherveou pgherveou enabled auto-merge June 1, 2026 07:42
@pgherveou pgherveou merged commit d9c28ad into main Jun 1, 2026
11 checks passed
@pgherveou pgherveou deleted the pg/fix-versioned-rust-types branch June 1, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants