use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Attribute, Block, Generics, Item, ItemFn, Path, Result, ReturnType, Type};
pub struct MainFn {
pub block: Box<Block>,
pub attrs: Vec<Attribute>,
pub return_type: Box<Type>,
pub generics: Generics,
}
impl Parse for MainFn {
fn parse(input: ParseStream) -> Result<Self> {
let parsed: Item = input.parse()?;
match parsed {
Item::Fn(func) => {
let ItemFn {
attrs, sig, block, ..
} = func;
if sig.asyncness.is_some() {
return Err(syn::Error::new_spanned(
sig.asyncness,
"the entrypoint can't be async",
));
}
if sig.constness.is_some() {
return Err(syn::Error::new_spanned(
sig.constness,
"the entrypoint can't be a const function",
));
}
if sig.abi.is_some() {
return Err(syn::Error::new_spanned(
sig.abi,
"the entrypoint can't be an external function",
));
}
let return_type = match sig.output {
ReturnType::Default => {
return Err(syn::Error::new_spanned(
sig,
"the entrypoint must return an instance of `PerseusAppBase` or one of its aliases (e.g. `PerseusApp`)",
))
}
ReturnType::Type(_, ty) => ty,
};
let inputs = sig.inputs;
if !inputs.is_empty() {
return Err(syn::Error::new_spanned(
inputs,
"the entrypoint can't take any arguments",
));
}
Ok(Self {
block,
attrs,
return_type,
generics: sig.generics,
})
}
item => Err(syn::Error::new_spanned(
item,
"only functions can be used as entrypoints",
)),
}
}
}
pub struct EngineMainFn {
pub block: Box<Block>,
pub attrs: Vec<Attribute>,
pub generics: Generics,
}
impl Parse for EngineMainFn {
fn parse(input: ParseStream) -> Result<Self> {
let parsed: Item = input.parse()?;
match parsed {
Item::Fn(func) => {
let ItemFn {
attrs, sig, block, ..
} = func;
if sig.asyncness.is_none() {
return Err(syn::Error::new_spanned(
sig.asyncness,
"the engine entrypoint must be async",
));
}
if sig.constness.is_some() {
return Err(syn::Error::new_spanned(
sig.constness,
"the entrypoint can't be a const function",
));
}
if sig.abi.is_some() {
return Err(syn::Error::new_spanned(
sig.abi,
"the entrypoint can't be an external function",
));
}
match sig.output {
ReturnType::Default => (),
ReturnType::Type(_, _) => {
return Err(syn::Error::new_spanned(
sig,
"the engine entrypoint must have no return value",
))
}
};
let inputs = sig.inputs;
if !inputs.is_empty() {
return Err(syn::Error::new_spanned(
inputs,
"the entrypoint can't take any arguments",
));
}
Ok(Self {
block,
attrs,
generics: sig.generics,
})
}
item => Err(syn::Error::new_spanned(
item,
"only functions can be used as entrypoints",
)),
}
}
}
pub fn main_impl(input: MainFn, server_fn: Path) -> TokenStream {
let MainFn {
block,
generics,
attrs,
return_type,
} = input;
let output = quote! {
#[cfg(engine)]
#[tokio::main]
async fn main() {
let op = ::perseus::engine::get_op().unwrap();
let exit_code = ::perseus::engine::run_dflt_engine(op, __perseus_simple_main, #server_fn).await;
std::process::exit(exit_code);
}
#[cfg(client)]
pub fn main() -> ::perseus::client::ClientReturn {
::perseus::client::run_client(__perseus_simple_main);
Ok(())
}
#(#attrs)*
#[doc(hidden)]
pub fn __perseus_simple_main #generics() -> #return_type {
#block
}
};
output
}
pub fn main_export_impl(input: MainFn) -> TokenStream {
let MainFn {
block,
generics,
attrs,
return_type,
} = input;
let output = quote! {
#[cfg(engine)]
#[tokio::main]
async fn main() {
let op = ::perseus::engine::get_op().unwrap();
let exit_code = ::perseus::engine::run_dflt_engine_export_only(op, __perseus_simple_main).await;
std::process::exit(exit_code);
}
#[cfg(client)]
pub fn main() -> ::perseus::client::ClientReturn {
::perseus::client::run_client(__perseus_simple_main);
Ok(())
}
#(#attrs)*
#[doc(hidden)]
pub fn __perseus_simple_main #generics() -> #return_type {
#block
}
};
output
}
pub fn browser_main_impl(input: MainFn) -> TokenStream {
let MainFn {
block,
attrs,
return_type,
..
} = input;
let output = quote! {
#[cfg(client)]
#(#attrs)*
pub fn main() -> #return_type {
#block
}
};
output
}
pub fn engine_main_impl(input: EngineMainFn) -> TokenStream {
let EngineMainFn { block, attrs, .. } = input;
let output = quote! {
#[cfg(engine)]
#[tokio::main]
#(#attrs)*
async fn main() {
#block
}
};
output
}