Skip to content

Commit

Permalink
Migrate to new macro.
Browse files Browse the repository at this point in the history
  • Loading branch information
futursolo committed Jul 29, 2023
1 parent b7e888b commit b1644c2
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 151 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/yew-agent-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "1", features = ["full","extra-traits"] }
syn = { version = "2", features = ["full", "extra-traits"] }
proc-macro-error = "1"

[dev-dependencies]
Expand Down
17 changes: 7 additions & 10 deletions packages/yew-agent-macro/src/agent_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub trait AgentFnType {
return Err(syn::Error::new_spanned(
params,
format!(
"{} agents can accept at most {} argument{}",
"{} agent can accept at most {} argument{}",
Self::agent_type_name(),
expected_len,
if expected_len > 1 { "s" } else { "" }
Expand All @@ -69,6 +69,7 @@ where
pub attrs: Vec<Attribute>,
pub name: Ident,
pub agent_name: Option<Ident>,
pub is_async: bool,

pub func: ItemFn,
}
Expand Down Expand Up @@ -108,13 +109,6 @@ where
));
}

if sig.asyncness.is_none() {
return Err(syn::Error::new_spanned(
sig.asyncness,
format!("{} functions must be async", F::agent_type_name()),
));
}

if sig.constness.is_some() {
return Err(syn::Error::new_spanned(
sig.constness,
Expand All @@ -131,10 +125,13 @@ where
let recv_type = F::parse_recv_type(&sig)?;
let output_type = F::parse_output_type(&sig)?;

let is_async = sig.asyncness.is_some();

Ok(Self {
recv_type,
output_type,
generics: sig.generics,
is_async,
vis,
attrs,
name: sig.ident,
Expand All @@ -153,7 +150,7 @@ where
self.attrs
.iter()
.filter_map(|m| {
m.path
m.path()
.get_ident()
.and_then(|ident| match ident.to_string().as_str() {
"doc" | "allow" => Some(m.clone()),
Expand All @@ -168,7 +165,7 @@ where
self.attrs
.iter()
.filter_map(|m| {
m.path
m.path()
.get_ident()
.and_then(|ident| match ident.to_string().as_str() {
"allow" => Some(m.clone()),
Expand Down
12 changes: 5 additions & 7 deletions packages/yew-agent-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ use proc_macro::TokenStream;
use syn::parse_macro_input;

mod agent_fn;
mod oneshot;
mod reactor;
mod task;

use agent_fn::{AgentFn, AgentName};
use oneshot::{oneshot_impl, OneshotFn};
use reactor::{reactor_impl, ReactorFn};
use task::{task_impl, TaskFn};

#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn reactor(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as AgentFn<ReactorFn>);
Expand All @@ -20,13 +19,12 @@ pub fn reactor(attr: TokenStream, item: TokenStream) -> TokenStream {
.into()
}

#[proc_macro_error::proc_macro_error]
#[proc_macro_attribute]
pub fn task(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as AgentFn<TaskFn>);
pub fn oneshot(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as AgentFn<OneshotFn>);
let attr = parse_macro_input!(attr as AgentName);

task_impl(attr, item)
oneshot_impl(attr, item)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
130 changes: 130 additions & 0 deletions packages/yew-agent-macro/src/oneshot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{parse_quote, Ident, ReturnType, Signature, Type};

use crate::agent_fn::{AgentFn, AgentFnType, AgentName};

pub struct OneshotFn {}

impl AgentFnType for OneshotFn {
type OutputType = Type;
type RecvType = Type;

fn attr_name() -> &'static str {
"oneshot"
}

fn agent_type_name() -> &'static str {
"oneshot"
}

fn parse_recv_type(sig: &Signature) -> syn::Result<Self::RecvType> {
let mut inputs = sig.inputs.iter();
let arg = inputs
.next()
.ok_or_else(|| syn::Error::new_spanned(&sig.ident, "expected 1 argument"))?;

let ty = Self::extract_fn_arg_type(arg)?;

Self::assert_no_left_argument(inputs, 1)?;

Ok(ty)
}

fn parse_output_type(sig: &Signature) -> syn::Result<Self::OutputType> {
let ty = match &sig.output {
ReturnType::Default => {
parse_quote! { () }
}
ReturnType::Type(_, ty) => *ty.clone(),
};

Ok(ty)
}
}

pub fn oneshot_impl(name: AgentName, mut agent_fn: AgentFn<OneshotFn>) -> syn::Result<TokenStream> {
agent_fn.merge_agent_name(name)?;

let struct_attrs = agent_fn.filter_attrs_for_agent_struct();
let oneshot_impl_attrs = agent_fn.filter_attrs_for_agent_impl();
let phantom_generics = agent_fn.phantom_generics();
let oneshot_name = agent_fn.agent_name();
let fn_name = agent_fn.inner_fn_ident();
let inner_fn = agent_fn.print_inner_fn();

let AgentFn {
recv_type: input_type,
generics,
output_type,
vis,
is_async,
..
} = agent_fn;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let fn_generics = ty_generics.as_turbofish();

let in_ident = Ident::new("_input", Span::mixed_site());

let fn_call = if is_async {
quote! { #fn_name #fn_generics (#in_ident).await }
} else {
quote! { #fn_name #fn_generics (#in_ident) }
};
let crate_name = quote! { ::yew_agent };

let quoted = quote! {
#(#struct_attrs)*
#[allow(unused_parens)]
#vis struct #oneshot_name #generics #where_clause {
inner: ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = #output_type>>>,
_marker: ::std::marker::PhantomData<(#phantom_generics)>,
}

// we cannot disable any lints here because it will be applied to the function body
// as well.
#(#oneshot_impl_attrs)*
impl #impl_generics ::#crate_name::oneshot::Oneshot for #oneshot_name #ty_generics #where_clause {
type Input = #input_type;

fn create(#in_ident: Self::Input) -> Self {
#inner_fn

Self {
inner: ::std::boxed::Box::pin(
async move {
#fn_call
}
),
_marker: ::std::marker::PhantomData,
}
}
}

impl #impl_generics ::std::future::Future for #oneshot_name #ty_generics #where_clause {
type Output = #output_type;

fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>) -> ::std::task::Poll<Self::Output> {
::std::future::Future::poll(::std::pin::Pin::new(&mut self.inner), cx)
}
}

impl #impl_generics ::#crate_name::Registrable for #oneshot_name #ty_generics #where_clause {
type Registrar = ::#crate_name::oneshot::OneshotRegistrar<Self>;

fn registrar() -> Self::Registrar {
::#crate_name::oneshot::OneshotRegistrar::<Self>::new()
}
}

impl #impl_generics ::#crate_name::Spawnable for #oneshot_name #ty_generics #where_clause {
type Spawner = ::#crate_name::oneshot::OneshotSpawner<Self>;

fn spawner() -> Self::Spawner {
::#crate_name::oneshot::OneshotSpawner::<Self>::new()
}
}
};

Ok(quoted)
}
76 changes: 49 additions & 27 deletions packages/yew-agent-macro/src/reactor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct ReactorFn {}

impl AgentFnType for ReactorFn {
type OutputType = ();
type RecvType = (Type, Type);
type RecvType = Type;

fn attr_name() -> &'static str {
"reactor"
Expand All @@ -20,19 +20,15 @@ impl AgentFnType for ReactorFn {

fn parse_recv_type(sig: &Signature) -> syn::Result<Self::RecvType> {
let mut inputs = sig.inputs.iter();
let arg1 = inputs
let arg = inputs
.next()
.ok_or_else(|| syn::Error::new_spanned(&sig.ident, "expected 2 arguments"))?;
let arg2 = inputs
.next()
.ok_or_else(|| syn::Error::new_spanned(&sig.ident, "expected 2 arguments"))?;
.ok_or_else(|| syn::Error::new_spanned(&sig.ident, "expected 1 argument"))?;

let ty1 = Self::extract_fn_arg_type(arg1)?;
let ty2 = Self::extract_fn_arg_type(arg2)?;
let ty = Self::extract_fn_arg_type(arg)?;

Self::assert_no_left_argument(inputs, 2)?;
Self::assert_no_left_argument(inputs, 1)?;

Ok((ty1, ty2))
Ok(ty)
}

fn parse_output_type(sig: &Signature) -> syn::Result<Self::OutputType> {
Expand All @@ -41,7 +37,7 @@ impl AgentFnType for ReactorFn {
ReturnType::Type(_, ty) => {
return Err(syn::Error::new_spanned(
ty,
"reactor functions cannot return any value",
"reactor agents cannot return any value",
))
}
}
Expand All @@ -53,6 +49,13 @@ impl AgentFnType for ReactorFn {
pub fn reactor_impl(name: AgentName, mut agent_fn: AgentFn<ReactorFn>) -> syn::Result<TokenStream> {
agent_fn.merge_agent_name(name)?;

if !agent_fn.is_async {
return Err(syn::Error::new_spanned(
&agent_fn.name,
"reactor agents must be asynchronous",
));
}

let struct_attrs = agent_fn.filter_attrs_for_agent_struct();
let reactor_impl_attrs = agent_fn.filter_attrs_for_agent_impl();
let phantom_generics = agent_fn.phantom_generics();
Expand All @@ -66,45 +69,64 @@ pub fn reactor_impl(name: AgentName, mut agent_fn: AgentFn<ReactorFn>) -> syn::R
vis,
..
} = agent_fn;

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let fn_generics = ty_generics.as_turbofish();
let (tx_type, rx_type) = recv_type;

let rx_ident = Ident::new("rx", Span::mixed_site());
let tx_ident = Ident::new("tx", Span::mixed_site());
let scope_ident = Ident::new("_scope", Span::mixed_site());

let fn_call = quote! { #fn_name #fn_generics (#tx_ident, #rx_ident).await; };
let fn_call = quote! { #fn_name #fn_generics (#scope_ident).await };
let crate_name = quote! { ::yew_agent };

let quoted = quote! {
#(#struct_attrs)*
#[allow(unused_parens)]
#vis struct #reactor_name #generics #where_clause {
inner: ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ()>>>,
_marker: ::std::marker::PhantomData<(#phantom_generics)>,
}

// we cannot disable any lints here because it will be applied to the function body
// as well.
#(#reactor_impl_attrs)*
impl #impl_generics ::yew_agent::reactor::Reactor for #reactor_name #ty_generics #where_clause {
type Sender = #tx_type;
type Receiver = #rx_type;
impl #impl_generics ::#crate_name::reactor::Reactor for #reactor_name #ty_generics #where_clause {
type Scope = #recv_type;

fn run(#tx_ident: Self::Sender, #rx_ident: Self::Receiver) -> ::yew_agent::__vendored::futures::future::LocalBoxFuture<'static, ()> {
fn create(#scope_ident: Self::Scope) -> Self {
#inner_fn

::yew_agent::__vendored::futures::future::FutureExt::boxed_local(
async move {
#fn_call
}
)
Self {
inner: ::std::boxed::Box::pin(
async move {
#fn_call
}
),
_marker: ::std::marker::PhantomData,
}
}
}

impl #impl_generics ::std::future::Future for #reactor_name #ty_generics #where_clause {
type Output = ();

fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>) -> ::std::task::Poll<Self::Output> {
::std::future::Future::poll(::std::pin::Pin::new(&mut self.inner), cx)
}
}

impl #impl_generics ::yew_agent::Registrable for #reactor_name #ty_generics #where_clause {
type Registrar = ::yew_agent::reactor::ReactorRegistrar<Self>;
impl #impl_generics ::#crate_name::Registrable for #reactor_name #ty_generics #where_clause {
type Registrar = ::#crate_name::reactor::ReactorRegistrar<Self>;

fn registrar() -> Self::Registrar {
::yew_agent::reactor::ReactorRegistrar::<Self>::new()
::#crate_name::reactor::ReactorRegistrar::<Self>::new()
}
}

impl #impl_generics ::#crate_name::Spawnable for #reactor_name #ty_generics #where_clause {
type Spawner = ::#crate_name::reactor::ReactorSpawner<Self>;

fn spawner() -> Self::Spawner {
::#crate_name::reactor::ReactorSpawner::<Self>::new()
}
}
};
Expand Down
Loading

0 comments on commit b1644c2

Please sign in to comment.