feat(v1): harden typed runtime boundaries

Elect one canonical EffectBatch codec, remove the parallel postcard API, and strengthen fail-closed host, form, sync, WASM, macro, generated-contract, and test-harness proofs with mutation-driven coverage.

req: wire/008

req: wire/009

req: wire/010

req: push/008

req: client_local/015

req: client_local/016

req: client_local/017

req: client_local/018

req: client_local/019

req: sync/024

req: sync/025

req: sync/026

req: sync/027

req: sync/028

req: sync/029

req: test/020

req: test/021
This commit is contained in:
slhx agent
2026-07-16 22:27:28 +02:00
parent e7211df4c5
commit e9ced4e0c1
19 changed files with 1529 additions and 198 deletions
+110 -25
View File
@@ -1,55 +1,52 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, Error, FnArg, ItemFn, Pat, ReturnType};
use syn::{Error, FnArg, ItemFn, Pat, ReturnType};
#[proc_macro_attribute]
pub fn presence(attributes: TokenStream, item: TokenStream) -> TokenStream {
expand_presence(attributes.into(), item.into())
.unwrap_or_else(Error::into_compile_error)
.into()
}
fn expand_presence(attributes: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
if !attributes.is_empty() {
return Error::new(
return Err(Error::new(
proc_macro2::Span::call_site(),
"#[hemx_sync::presence] does not accept arguments",
)
.to_compile_error()
.into();
));
}
let mut function = parse_macro_input!(item as ItemFn);
if function.sig.asyncness.is_some() {
return Error::new_spanned(
function.sig.asyncness,
let mut function: ItemFn = syn::parse2(item)?;
if let Some(asyncness) = &function.sig.asyncness {
return Err(Error::new_spanned(
asyncness,
"presence projections must be synchronous",
)
.to_compile_error()
.into();
));
}
if matches!(function.sig.output, ReturnType::Default) {
return Error::new_spanned(
return Err(Error::new_spanned(
&function.sig,
"presence projections must return impl IntoEffect",
)
.to_compile_error()
.into();
));
}
let argument = match function.sig.inputs.first() {
Some(FnArg::Typed(argument)) if function.sig.inputs.len() == 1 => argument,
_ => {
return Error::new_spanned(
return Err(Error::new_spanned(
&function.sig.inputs,
"presence projections require exactly one typed presence argument",
)
.to_compile_error()
.into();
));
}
};
let argument_name = match argument.pat.as_ref() {
Pat::Ident(argument) => argument.ident.clone(),
pattern => {
return Error::new_spanned(
return Err(Error::new_spanned(
pattern,
"presence projection argument must be a simple identifier",
)
.to_compile_error()
.into();
));
}
};
@@ -61,5 +58,93 @@ pub fn presence(attributes: TokenStream, item: TokenStream) -> TokenStream {
let __hemx_sync_effect = (|| #body)();
::hemx_sync::PresenceProjection::new(__hemx_sync_channel, __hemx_sync_effect)
}));
quote!(#function).into()
Ok(quote!(#function))
}
#[cfg(test)]
mod tests {
use super::expand_presence;
use quote::quote;
#[test]
fn presence_expansion_enforces_the_typed_projection_contract() {
assert!(expand_presence(quote!(), quote!(not a function)).is_err());
for (attributes, item, expected) in [
(
quote!(unexpected),
quote!(
fn project(scope: Scope) -> Effect {
effect()
}
),
"#[hemx_sync::presence] does not accept arguments",
),
(
quote!(),
quote!(
async fn project(scope: Scope) -> Effect {
effect()
}
),
"presence projections must be synchronous",
),
(
quote!(),
quote!(
fn project(scope: Scope) {}
),
"presence projections must return impl IntoEffect",
),
(
quote!(),
quote!(
fn project() -> Effect {
effect()
}
),
"presence projections require exactly one typed presence argument",
),
(
quote!(),
quote!(
fn project(a: Scope, b: Scope) -> Effect {
effect()
}
),
"presence projections require exactly one typed presence argument",
),
(
quote!(),
quote!(
fn project((scope,): (Scope,)) -> Effect {
effect()
}
),
"presence projection argument must be a simple identifier",
),
] {
assert_eq!(
expand_presence(attributes, item).unwrap_err().to_string(),
expected
);
}
let expanded = expand_presence(
quote!(),
quote!(
pub fn project(scope: Scope) -> Effect {
effect(scope)
}
),
)
.unwrap()
.to_string();
assert!(expanded.contains("pub fn project"));
assert!(expanded.contains("impl :: hemx_sync :: PresenceUpdate"));
assert!(expanded.contains("PresenceScope :: presence_channel (& scope)"));
assert!(expanded.contains("PresenceProjection :: new"));
assert!(expanded.contains("effect (scope)"));
// test req: sync/003 req: sync/005
}
}