feat(sync): add typed presence projection
req: sync/001 req: sync/005
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "hemx-sync-macros"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
@@ -0,0 +1,65 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Error, FnArg, ItemFn, Pat, ReturnType};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn presence(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
if !attributes.is_empty() {
|
||||
return 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,
|
||||
"presence projections must be synchronous",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
if matches!(function.sig.output, ReturnType::Default) {
|
||||
return 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(
|
||||
&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(
|
||||
pattern,
|
||||
"presence projection argument must be a simple identifier",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
let body = function.block;
|
||||
function.sig.output = syn::parse_quote!(-> impl ::hemx_sync::PresenceUpdate);
|
||||
function.block = Box::new(syn::parse_quote!({
|
||||
let __hemx_sync_channel =
|
||||
::hemx_sync::PresenceScope::presence_channel(&#argument_name);
|
||||
let __hemx_sync_effect = (|| #body)();
|
||||
::hemx_sync::PresenceProjection::new(__hemx_sync_channel, __hemx_sync_effect)
|
||||
}));
|
||||
quote!(#function).into()
|
||||
}
|
||||
Reference in New Issue
Block a user