use proc_macro::TokenStream;
use proc_macro2::{Literal, Span, TokenStream as TokenStream2, TokenTree};
use quote::quote;
use syn::{
Block, DeriveInput, Expr, ExprClosure, Ident, ImplItem, ItemImpl, LitStr, Pat, Result,
ReturnType, Stmt, Token, Type, TypePath, Variant,
fold::fold_block,
fold::{Fold, fold_expr, fold_stmt},
parse_macro_input, parse_quote, parse2,
};
#[derive(Debug, Clone)]
#[allow(dead_code)]
enum UuidArg {
Literal(LitStr),
Const(Expr),
}
impl syn::parse::Parse for ActorArgs {
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
let uuid = if input.peek(LitStr) {
UuidArg::Literal(input.parse::<LitStr>()?)
} else {
UuidArg::Const(input.parse::<Expr>()?)
};
let mut snapshot = None;
let mut feature = None;
let mut ts = false;
while input.peek(Token![,]) {
input.parse::<Token![,]>()?;
if input.is_empty() {
break;
}
if !input.peek(Ident) {
continue;
}
let ident: Ident = input.parse()?;
match ident.to_string().as_str() {
"snapshot" => {
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let snapshot_type: TypePath = input.parse()?;
snapshot = Some(SnapshotArg::Explicit(snapshot_type));
} else {
snapshot = Some(SnapshotArg::UseSelf);
}
}
"feature" => {
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let feature_str: LitStr = input.parse()?;
feature = Some(feature_str);
} else {
return Err(syn::Error::new_spanned(
ident,
"Expected 'feature = \"name\"'",
));
}
}
"ts" => {
ts = true;
}
_ => {
return Err(syn::Error::new_spanned(
ident,
"Expected 'snapshot', 'feature', or 'ts'",
));
}
}
}
Ok(Self {
uuid,
snapshot,
feature,
ts,
})
}
}
pub fn actor_impl(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as ActorArgs);
let input_tokens = TokenStream2::from(input);
let input = match parse2::<ItemImpl>(input_tokens) {
Err(err) => return TokenStream::from(err.to_compile_error()),
Ok(input) => input,
};
if let Err(err) = validate_actor_impl_structure(&input) {
return TokenStream::from(err.to_compile_error());
}
match generate_actor_impl(input, &args) {
Err(err) => TokenStream::from(err.to_compile_error()),
Ok(tokens) => TokenStream::from(tokens),
}
}
pub fn derive_actor_args_impl(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
generate_actor_args_impl(&input).into()
}
#[derive(Debug, Clone)]
enum SnapshotArg {
UseSelf,
Explicit(TypePath),
}
#[derive(Debug, Clone)]
struct ActorArgs {
uuid: UuidArg,
snapshot: Option<SnapshotArg>,
feature: Option<LitStr>,
#[allow(dead_code)]
ts: bool,
}
#[derive(Debug, Clone)]
struct AsyncClosure {
param_pattern: Pat,
param_type: TypePath,
return_type: Option<Type>,
body: Block,
}
fn validate_actor_impl_structure(input: &ItemImpl) -> Result<()> {
let is_actor_impl = matches!(
&input.trait_, Some((_bang, path, _for)) if path.segments.last().is_some_and(|s| s.ident == "Actor")
);
if !is_actor_impl {
return Err(syn::Error::new_spanned(
&input.self_ty,
"#[actor(...)] must be applied to `impl ::theta::actor::Actor for T { ... }`",
));
}
for item in &input.items {
let syn::ImplItem::Const(const_item) = item else {
continue;
};
if !matches!(const_item.ident.to_string().as_str(), "_") {
continue;
}
match &const_item.expr {
syn::Expr::Block(block_expr) => {
validate_closures_in_block(&block_expr.block)?;
}
syn::Expr::Closure(closure) => {
if closure.asyncness.is_some() {
validate_async_closure(closure)?;
} else {
return Err(syn::Error::new_spanned(closure, "Expected async closure"));
}
}
_ => {
return Err(syn::Error::new_spanned(
&const_item.expr,
"Expected block expression with async closures or direct async closure",
));
}
}
}
Ok(())
}
fn validate_closures_in_block(block: &Block) -> Result<()> {
for stmt in &block.stmts {
let Stmt::Expr(expr, _) = stmt else { continue };
validate_closure_expr(expr)?;
}
Ok(())
}
fn validate_closure_expr(expr: &Expr) -> Result<()> {
match expr {
Expr::Closure(closure) => {
if closure.asyncness.is_some() {
validate_async_closure(closure)?;
}
}
_ => return Err(syn::Error::new_spanned(expr, "Expected async closures")),
}
Ok(())
}
fn validate_async_closure(closure: &ExprClosure) -> Result<()> {
if closure.inputs.len() != 1 {
return Err(syn::Error::new_spanned(
closure,
"Message handler closure must have exactly one parameter",
));
}
let param = closure.inputs.first().unwrap();
validate_param_pattern(param)?;
Ok(())
}
fn validate_param_pattern(param: &Pat) -> Result<()> {
match param {
Pat::Type(pat_type) => {
let Type::Path(_) = &*pat_type.ty else {
return Err(syn::Error::new_spanned(
pat_type,
"Parameter type must be a path type",
));
};
Ok(())
}
Pat::Struct(_) | Pat::TupleStruct(_) => Ok(()),
_ => Err(syn::Error::new_spanned(
param,
"Parameter must be typed or destructuring pattern",
)),
}
}
fn generate_actor_args_impl(input: &DeriveInput) -> TokenStream2 {
let name = &input.ident;
let expanded = quote! {
impl ::theta::actor::ActorArgs for # name { type Actor = Self; async fn initialize(ctx :
::theta::context::Context < Self::Actor >, cfg : &Self,) -> Self::Actor { cfg.clone() } }
impl ::core::convert::From <&# name > for # name { fn from(actor : &# name) -> Self { actor
.clone() } }
};
expanded
}
fn generate_actor_impl(mut input: ItemImpl, args: &ActorArgs) -> Result<TokenStream2> {
let actor_type = extract_actor_type(&input)?;
let async_closures = extract_async_closures_from_impl(&input)?;
let view = extract_view(&input)?;
let enum_ident = generate_enum_message_ident(&actor_type);
let param_types: Vec<&TypePath> = async_closures.iter().map(|c| &c.param_type).collect();
let variant_idents: Vec<Ident> = async_closures
.iter()
.map(|c| generate_enum_message_variant_ident(&c.param_type, enum_ident.span()))
.collect();
let allow_unused = match &args.feature {
None => quote! {},
Some(feature) => {
quote! {
#[cfg_attr(not(feature = # feature), allow(unused_variables))]
}
}
};
let process_msg_impl_ts =
generate_process_msg_impl(&variant_idents, args.feature.as_ref(), &allow_unused);
let enum_message = generate_enum_message(&enum_ident, &variant_idents, ¶m_types)?;
#[cfg(feature = "remote")]
let from_tagged_bytes_impl =
generate_from_tagged_bytes_impl(&enum_ident, ¶m_types, &variant_idents);
#[cfg(not(feature = "remote"))]
let from_tagged_bytes_impl = quote! {};
let message_impls = generate_message_impls(
&actor_type,
&async_closures,
args.feature.as_ref(),
&allow_unused,
);
let into_impls = generate_into_impls(&enum_ident, ¶m_types, &variant_idents)?;
let persistent_actor_impl = match &args.snapshot {
None => quote! {},
Some(SnapshotArg::UseSelf) => {
let self_path: TypePath = parse_quote!(# actor_type);
generate_persistent_actor_impl(&actor_type, &self_path)
}
Some(SnapshotArg::Explicit(explicit_type)) => {
generate_persistent_actor_impl(&actor_type, explicit_type)
}
};
#[cfg(feature = "ts")]
let ts_bindings = build_ts_bindings(
args,
&actor_type,
&view,
&async_closures,
&variant_idents,
&enum_ident,
);
#[cfg(not(feature = "ts"))]
let ts_bindings = quote! {};
input.items.retain(|it| {
!matches!(
it, ImplItem::Const(c) if c.ident == "_" && (matches!(c.expr, Expr::Block(_))
||matches ! (c.expr, Expr::Closure(_)))
)
});
let hash_assertion = push_generated_items(
&mut input,
&enum_ident,
&view,
&actor_type,
process_msg_impl_ts,
args,
)?;
Ok(quote! {
# input # enum_message # from_tagged_bytes_impl # (# message_impls) * # (# into_impls) *
# persistent_actor_impl # hash_assertion # ts_bindings
})
}
#[cfg(feature = "ts")]
fn build_ts_bindings(
args: &ActorArgs,
actor_type: &Ident,
view: &TypePath,
async_closures: &[AsyncClosure],
variant_idents: &[Ident],
enum_ident: &Ident,
) -> TokenStream2 {
if args.ts {
let msg_infos: Vec<crate::ts::TsMsgInfo> = async_closures
.iter()
.zip(variant_idents.iter())
.map(|(closure, variant)| crate::ts::TsMsgInfo {
rust_type_name: closure
.param_type
.path
.segments
.last()
.map(|s| s.ident.to_string())
.unwrap_or_default(),
variant_ident: variant.clone(),
return_type: closure.return_type.clone(),
})
.collect();
crate::ts::generate_ts_bindings(actor_type, view, &msg_infos, enum_ident)
} else {
quote! {}
}
}
fn push_generated_items(
input: &mut ItemImpl,
enum_ident: &Ident,
view: &TypePath,
actor_type: &Ident,
process_msg_impl_ts: TokenStream2,
args: &ActorArgs,
) -> Result<TokenStream2> {
let mut has_msg_type = false;
let mut has_view_type = false;
let mut has_hash_code = false;
let mut has_process_msg = false;
let mut has_impl_id = false;
for item in &input.items {
match item {
ImplItem::Type(t) if t.ident == "Msg" => has_msg_type = true,
ImplItem::Type(t) if t.ident == "View" => has_view_type = true,
ImplItem::Fn(f) if f.sig.ident == "hash_code" => has_hash_code = true,
ImplItem::Fn(f) if f.sig.ident == "process_msg" => has_process_msg = true,
ImplItem::Const(c) if c.ident == "IMPL_ID" => has_impl_id = true,
_ => {}
}
}
if !has_msg_type {
input.items.push(parse_quote!(type Msg = # enum_ident;));
}
let hash_assertion = if !has_view_type {
input.items.push(parse_quote!(type View = # view;));
quote! {}
} else if !has_hash_code {
let hash_code_impl = generate_hash_code_impl();
input.items.push(hash_code_impl);
quote! {
const _ : () = { fn _assert_hash < T : ::std::hash::Hash > () {} let _ =
_assert_hash::<# actor_type >; };
}
} else {
quote! {}
};
if !has_process_msg {
let item_fn: ImplItem = parse2(process_msg_impl_ts)?;
input.items.push(item_fn);
}
if !has_impl_id {
#[cfg(feature = "remote")]
{
let uuid_expr = match &args.uuid {
UuidArg::Literal(lit) => quote! { ::theta::__private::uuid::uuid!(#lit) },
UuidArg::Const(expr) => quote! { #expr },
};
input.items.push(parse_quote!(
const IMPL_ID: ::theta::remote::base::ActorTypeId = #uuid_expr;
));
}
#[cfg(not(feature = "remote"))]
{
let _uuid = &args.uuid;
}
}
Ok(hash_assertion)
}
fn extract_actor_type(input: &ItemImpl) -> Result<Ident> {
let Type::Path(type_path) = &*input.self_ty else {
return Err(syn::Error::new_spanned(
&input.self_ty,
"Actor type must be a path",
));
};
let Some(segment) = type_path.path.segments.last() else {
return Err(syn::Error::new_spanned(
&input.self_ty,
"Invalid actor type",
));
};
Ok(segment.ident.clone())
}
fn extract_async_closures_from_impl(input: &ItemImpl) -> Result<Vec<AsyncClosure>> {
let mut closures = Vec::new();
for item in &input.items {
let syn::ImplItem::Const(const_item) = item else {
continue;
};
if !matches!(const_item.ident.to_string().as_str(), "_") {
continue;
}
match &const_item.expr {
syn::Expr::Block(block_expr) => {
extract_closures_from_block(&block_expr.block, &mut closures)?;
}
syn::Expr::Closure(closure) => {
if closure.asyncness.is_some() {
let async_closure = parse_async_closure(closure)?;
closures.push(async_closure);
}
}
_ => {
return Err(syn::Error::new_spanned(
&const_item.expr,
"Expected block expression with async closures or direct async closure",
));
}
}
}
Ok(closures)
}
fn extract_view(input: &ItemImpl) -> Result<TypePath> {
for item in &input.items {
let syn::ImplItem::Type(type_item) = item else {
continue;
};
if type_item.ident != "View" {
continue;
}
let Type::Path(type_path) = &type_item.ty else {
return Err(syn::Error::new_spanned(
&type_item.ty,
"View must be a path type",
));
};
return Ok(type_path.clone());
}
Ok(parse_quote!(::theta::base::Nil))
}
fn extract_closures_from_block(block: &Block, closures: &mut Vec<AsyncClosure>) -> Result<()> {
for stmt in &block.stmts {
let Stmt::Expr(expr, _) = stmt else { continue };
extract_closures_from_expr(expr, closures)?;
}
Ok(())
}
fn extract_closures_from_expr(expr: &Expr, closures: &mut Vec<AsyncClosure>) -> Result<()> {
match expr {
Expr::Closure(closure) => {
if closure.asyncness.is_some() {
let async_closure = parse_async_closure(closure)?;
closures.push(async_closure);
}
}
_ => {
return Err(syn::Error::new_spanned(expr, "Expected async closures"));
}
}
Ok(())
}
fn parse_async_closure(closure: &ExprClosure) -> Result<AsyncClosure> {
if closure.inputs.len() != 1 {
return Err(syn::Error::new_spanned(
closure,
"Message handler closure must have exactly one parameter",
));
}
let param = closure.inputs.first().unwrap();
let (param_type, param_pattern) = extract_type_and_pattern(param)?;
let return_type = match &closure.output {
ReturnType::Type(_, ty) => Some((**ty).clone()),
ReturnType::Default => None,
};
let body = match &*closure.body {
Expr::Block(block_expr) => block_expr.block.clone(),
other => parse_quote!({ # other }),
};
Ok(AsyncClosure {
param_pattern,
param_type,
return_type,
body,
})
}
fn extract_type_and_pattern(param: &Pat) -> Result<(TypePath, Pat)> {
match param {
Pat::Type(pat_type) => {
let Type::Path(type_path) = &*pat_type.ty else {
return Err(syn::Error::new_spanned(
pat_type,
"Parameter type must be a path type",
));
};
Ok((type_path.clone(), (*pat_type.pat).clone()))
}
Pat::Struct(pat_struct) => Ok((
TypePath {
qself: pat_struct.qself.clone(),
path: pat_struct.path.clone(),
},
param.clone(),
)),
Pat::TupleStruct(pat_tuple_struct) => Ok((
TypePath {
qself: pat_tuple_struct.qself.clone(),
path: pat_tuple_struct.path.clone(),
},
param.clone(),
)),
_ => Err(syn::Error::new_spanned(
param,
"Parameter must be typed or destructuring pattern",
)),
}
}
fn generate_process_msg_impl(
message_enum_variant_idents: &[Ident],
feature: Option<&LitStr>,
allow_unused: &TokenStream2,
) -> TokenStream2 {
let match_arms: Vec<_> = message_enum_variant_idents
.iter()
.map(|variant_ident| {
let error_handling = {
quote! {
{ return ::theta::__private::tracing::error!("failed to serialize result: {e}");
}
}
};
let tell_arm = feature_gated(
feature,
quote! {
let _ = ::theta::message::Message::< Self >::process(self, ctx, m). await;
},
);
let ask_arm = feature_gated(
feature,
quote! {
{ let any_ret = ::theta::message::Message::< Self >::process_to_any(self, ctx,
m). await; let _ = tx.send(any_ret); }
},
);
let base_arms = quote! {
::theta::message::Continuation::Nil => { { # tell_arm } }
::theta::message::Continuation::Reply(tx) |
::theta::message::Continuation::Forward(tx) => { { # ask_arm } }
};
let remote_arms = if cfg!(feature = "remote") {
let bin_reply_arm = feature_gated(
feature,
quote! {
let bytes = match ::theta::message::Message::< Self
>::process_to_bytes(self, ctx, peer.clone(), m). await {
::std::result::Result::Err(e) => #
error_handling::std::result::Result::Ok(bytes) => bytes, }; let _ = reply_tx
.send(bytes);
},
);
let bin_forward_arm = feature_gated(
feature,
quote! {
let bytes = match ::theta::message::Message::< Self
>::process_to_bytes(self, ctx, peer.clone(), m). await {
::std::result::Result::Err(e) => #
error_handling::std::result::Result::Ok(bytes) => bytes, }; if let Err(_) =
tx.send(bytes) {
::theta::__private::tracing::error!("failed to send binary forward"); }
},
);
quote! {
::theta::message::Continuation::BinReply { peer, reply_tx } => { # bin_reply_arm
} ::theta::message::Continuation::LocalBinForward { peer, tx } |::
theta::message::Continuation::RemoteBinForward { peer, tx } => { #
bin_forward_arm }
}
} else {
quote! {}
};
quote! {
# allow_unused Self::Msg::# variant_ident(m) => { match k { # base_arms #
remote_arms } }
}
})
.collect();
quote! {
# allow_unused async fn process_msg(&mut self, ctx : &:: theta::context::Context < Self
>, msg : Self::Msg, k : ::theta::message::Continuation,) -> () { match msg { # (#
match_arms) * } }
}
}
fn generate_enum_message(
enum_ident: &Ident,
enum_message_variant_idents: &[Ident],
param_types: &[&TypePath],
) -> Result<TokenStream2> {
let enum_message_variants =
generate_enum_message_variants(enum_message_variant_idents, param_types)?;
Ok(quote! {
#[allow(non_camel_case_types)] #[derive(Debug, Clone,
::theta::__private::serde::Serialize, ::theta::__private::serde::Deserialize)] pub enum
# enum_ident { # (# enum_message_variants),* }
})
}
fn generate_enum_message_ident(name: &Ident) -> Ident {
syn::Ident::new(&format!("{name}__Msg"), name.span())
}
fn generate_enum_message_variants(
variant_idents: &[Ident],
param_types: &[&TypePath],
) -> Result<Vec<Variant>> {
variant_idents
.iter()
.zip(param_types)
.map(|(variant_ident, param_type)| {
Ok(parse_quote! {
# variant_ident(# param_type)
})
})
.collect()
}
fn generate_enum_message_variant_ident(ty: &TypePath, span: Span) -> Ident {
let mut joined = String::new();
for (i, seg) in ty.path.segments.iter().enumerate() {
if i > 0 {
joined.push(' ');
}
joined.push_str(&seg.ident.to_string());
}
let variant_name = format!("__{}", heck::AsUpperCamelCase(&joined));
syn::Ident::new(&variant_name, span)
}
#[cfg(feature = "remote")]
fn generate_from_tagged_bytes_impl(
enum_ident: &Ident,
param_types: &[&TypePath],
variant_idents: &[Ident],
) -> TokenStream2 {
let deserialize_fns: Vec<_> = param_types
.iter()
.enumerate()
.map(|(i, param_type)| {
let variant_ident = &variant_idents[i];
quote! {
|bytes| ::theta::__private::postcard::from_bytes::<# param_type > (bytes).map(|m| #
enum_ident::# variant_ident(m))
}
})
.collect();
let deserialize_fns = quote! {
const DESERIALIZE_FNS : &[fn (&[u8]) -> Result <# enum_ident,
::theta::__private::postcard::Error >] = &[# (# deserialize_fns),*];
};
quote! {
impl ::theta::remote::serde::FromTaggedBytes for # enum_ident { fn from(tag :
::theta::remote::base::Tag, bytes : &[u8]) -> Result < Self,
::theta::__private::postcard::Error > { # deserialize_fns let Some(deserialize_fn) =
DESERIALIZE_FNS.get(tag as usize) else { return
Err(::theta::__private::postcard::Error::SerdeDeCustom); }; deserialize_fn(bytes) } }
}
}
fn generate_message_impls(
actor_ident: &Ident,
async_closures: &[AsyncClosure],
feature: Option<&LitStr>,
allow_unused: &TokenStream2,
) -> Vec<TokenStream2> {
async_closures
.iter()
.enumerate()
.map(|(i, closure)| {
generate_single_message_impl(actor_ident, closure, i, feature, allow_unused)
})
.collect()
}
fn generate_single_message_impl(
actor_ident: &Ident,
closure: &AsyncClosure,
index: usize,
feature: Option<&LitStr>,
allow_unused: &TokenStream2,
) -> TokenStream2 {
let param_type = &closure.param_type;
let param_pattern = &closure.param_pattern;
let stmts = replace_self_with_state(&closure.body).stmts;
let return_type = match &closure.return_type {
None => {
quote! {
()
}
}
Some(ty) => {
quote! {
# ty
}
}
};
#[cfg(feature = "remote")]
let tag_const = {
#[allow(clippy::cast_possible_truncation)]
let idx = Literal::u32_suffixed(index as u32);
quote! {
const TAG : ::theta::remote::base::Tag = # idx;
}
};
#[cfg(not(feature = "remote"))]
let tag_const = {
#[allow(clippy::cast_possible_truncation)]
let _idx = Literal::u32_suffixed(index as u32);
quote! {}
};
let feature_gated_body = feature_gated(
feature,
quote! {
# (# stmts) *
},
);
quote! {
impl ::theta::message::Message <# actor_ident > for # param_type { type Return = #
return_type; # tag_const # allow_unused fn process(state : &mut # actor_ident, ctx : &::
theta::context::Context <# actor_ident >, # param_pattern : Self,) -> impl
::std::future::Future < Output = Self::Return > + Send { async move { #
feature_gated_body } } }
}
}
fn replace_self_with_state(block: &Block) -> Block {
struct SelfReplacer;
impl SelfReplacer {
fn replace_tokens_in_stream(tokens: TokenStream2) -> TokenStream2 {
let mut result = TokenStream2::new();
let tokens_iter = tokens.into_iter();
for token in tokens_iter {
match token {
TokenTree::Ident(ident) if ident == "self" => {
result.extend(std::iter::once(TokenTree::Ident(proc_macro2::Ident::new(
"state",
ident.span(),
))));
}
TokenTree::Group(group) => {
let replaced_stream = Self::replace_tokens_in_stream(group.stream());
result.extend(std::iter::once(TokenTree::Group(proc_macro2::Group::new(
group.delimiter(),
replaced_stream,
))));
}
other => {
result.extend(std::iter::once(other));
}
}
}
result
}
}
impl Fold for SelfReplacer {
fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
match stmt {
Stmt::Macro(mut stmt_macro) => {
stmt_macro.mac.tokens = Self::replace_tokens_in_stream(stmt_macro.mac.tokens);
Stmt::Macro(stmt_macro)
}
other => fold_stmt(self, other),
}
}
fn fold_expr(&mut self, expr: Expr) -> Expr {
match expr {
Expr::Path(mut expr_path) if expr_path.path.is_ident("self") => {
expr_path.path = parse_quote!(state);
Expr::Path(expr_path)
}
Expr::Field(mut expr_field) => {
expr_field.base = Box::new(self.fold_expr(*expr_field.base));
Expr::Field(expr_field)
}
Expr::MethodCall(mut expr_method_call) => {
expr_method_call.receiver =
Box::new(self.fold_expr(*expr_method_call.receiver));
expr_method_call.args = expr_method_call
.args
.into_iter()
.map(|arg| self.fold_expr(arg))
.collect();
Expr::MethodCall(expr_method_call)
}
Expr::Macro(mut expr_macro) => {
expr_macro.mac.tokens = Self::replace_tokens_in_stream(expr_macro.mac.tokens);
Expr::Macro(expr_macro)
}
other => fold_expr(self, other),
}
}
}
let mut replacer = SelfReplacer;
fold_block(&mut replacer, block.clone())
}
fn generate_into_impls(
enum_ident: &Ident,
param_types: &[&TypePath],
variant_idents: &[Ident],
) -> Result<Vec<TokenStream2>> {
param_types
.iter()
.zip(variant_idents)
.map(|(param_type, variant_ident)| {
Ok(quote! {
impl From <# param_type > for # enum_ident { fn from(msg : # param_type) -> Self
{ Self::# variant_ident(msg) } }
})
})
.collect()
}
fn generate_persistent_actor_impl(actor_type: &Ident, snapshot_type: &TypePath) -> TokenStream2 {
quote! {
impl ::theta::persistence::persistent_actor::PersistentActor for # actor_type { type
Snapshot = # snapshot_type; type RuntimeArgs = (); type ActorArgs = # snapshot_type; fn
persistent_args(snapshot : Self::Snapshot, _runtime_args : Self::RuntimeArgs,) ->
Self::ActorArgs { snapshot } }
}
}
fn feature_gated(feature: Option<&LitStr>, token: TokenStream2) -> TokenStream2 {
if let Some(feature_name) = feature {
quote! {
#[cfg(feature = # feature_name)] return { # token }; #[cfg(not(feature = #
feature_name))] ::std::unimplemented!("available with '#feature_name' feature")
}
} else {
token
}
}
fn generate_hash_code_impl() -> ImplItem {
parse_quote! {
fn hash_code(&self) -> u64 { let mut hasher =
::theta::__private::ahash::AHasher::default(); ::std::hash::Hash::hash(self, &mut
hasher); ::std::hash::Hasher::finish(&hasher) }
}
}