extern crate quote;
use proc_macro2::Span;
use syn::{
Ident, Lit, Meta, NestedMeta, Path, Result, parse::Parse, parse::ParseStream,
punctuated::Punctuated, token::Comma, token::Paren
};
use proc_macro::TokenStream;
use std::io::Write;
use std::fmt;
#[cfg(not(feature="force_rinecs_cratename"))]
use proc_macro_crate::crate_name;
use syn::spanned::Spanned;
use self::quote::{ToTokens, TokenStreamExt};
#[cfg(not(feature="force_rinecs_cratename"))]
fn rinecs_crate_name() -> syn::Result<syn::Path> {
let crate_name: String = crate_name("rinecs")
.or(crate_name("rin").map(|rin| rin + "::ecs"))
.unwrap_or("rin::ecs".to_string());
syn::parse_str(&crate_name)
}
#[cfg(feature="force_rinecs_cratename")]
fn rinecs_crate_name() -> syn::Result<syn::Path> {
syn::parse_str("rinecs")
}
fn _rustfmt<C: fmt::Display>(code: C) -> String{
let code = format!("{}", code);
let mut child = ::std::process::Command::new("rustfmt")
.stdin(::std::process::Stdio::piped())
.stdout(::std::process::Stdio::piped())
.spawn()
.expect("Failed to format source snippet");
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
stdin.write_all(code.as_bytes()).expect("Failed to write to stdin");
}
let out = child.wait_with_output().expect("Failed to format source snippet").stdout;
String::from_utf8_lossy(&out).to_string()
}
enum Bitmask{
Has(PathOrLiteral),
Or(PathOrLiteral),
HasOr(PathOrLiteral, PathOrLiteral),
HasNot(PathOrLiteral, PathOrLiteral),
Not(PathOrLiteral),
}
impl Parse for Bitmask{
fn parse(input: ParseStream) -> Result<Bitmask>{
let ident: Ident = input.parse()?;
let ident_str = format!("{}", ident);
let content;
let _paren = parenthesized!(content in input);
let bitmask = match ident_str.as_str() {
"has" => {
let has = content.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing has component"))?;
Bitmask::Has(has)
},
"or" => {
let or = content.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing or component"))?;
Bitmask::Or(or)
},
"has_or" => {
let has_or: Punctuated<PathOrLiteral, Comma> = content.parse_terminated(PathOrLiteral::parse)
.map_err(|err| syn::Error::new(err.span(), "Error parsing has_or components"))?;
let mut has_or = has_or.into_iter();
let has = has_or.next().expect("has_or with no components");
let or = has_or.next().expect("has_or with no not component");
Bitmask::HasOr(has, or)
},
"has_not" => {
let has_not: Punctuated<PathOrLiteral, Comma> = content.parse_terminated(PathOrLiteral::parse)
.map_err(|err| syn::Error::new(err.span(), "Error parsing has_not components"))?;
let mut has_not = has_not.into_iter();
let has = has_not.next().expect("has_or with no components");
let not = has_not.next().expect("has_or with no not component");
Bitmask::HasNot(has, not)
},
"not" => {
let not = content.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing not component"))?;
Bitmask::Not(not)
},
_ => panic!("Wrong syntax"),
};
Ok(bitmask)
}
}
impl Bitmask {
fn to_rinecs(&self) -> proc_macro2::TokenStream{
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return err.to_compile_error(),
};
match self{
Bitmask::Has(lit_or_path) => {
parse_quote!{
#crate_name::Bitmask::has(world.component_mask::<#lit_or_path>())
}
}
Bitmask::Or(lit_or_path) => {
parse_quote!{
#crate_name::Bitmask::or(world.component_mask::<#lit_or_path>())
}
}
Bitmask::HasOr(has, or) => {
parse_quote!{
#crate_name::Bitmask::has_or(
world.component_mask::<#has>(),
world.component_mask::<#or>())
}
}
Bitmask::HasNot(has, not) => {
parse_quote!{
#crate_name::Bitmask::has_not(
world.component_mask::<#has>(),
world.component_mask::<#not>())
}
}
Bitmask::Not(lit_or_path) => {
parse_quote!{
#crate_name::Bitmask::not(world.component_mask::<#lit_or_path>())
}
}
}
}
}
struct Operator{
pub ident: Ident,
pub paren: Paren,
pub operator: OperatorType,
}
enum OperatorType{
All(Punctuated<Operator, Comma>),
Any(Punctuated<Operator, Comma>),
Not(Box<Operator>),
Components(Punctuated<Bitmask, Comma>),
ComponentChanged(Lit),
ResourcesChanged(Lit),
HasResource(Lit),
}
impl OperatorType{
fn to_rinecs(&self) -> proc_macro2::TokenStream {
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return err.to_compile_error(),
};
match self{
OperatorType::All(operators) => {
let operators = operators.iter().map(|op| op.operator.to_rinecs());
parse_quote!{
#crate_name::SystemCondition::All(vec![
#( #operators, )*
])
}
}
OperatorType::Any(operators) => {
let operators = operators.iter().map(|op| op.operator.to_rinecs());
parse_quote!{
#crate_name::SystemCondition::Any(vec![
#( #operators, )*
])
}
}
OperatorType::Not(operator) => {
let operator = operator.operator.to_rinecs();
parse_quote!{
#crate_name::SystemCondition::Not(#operator)
}
}
OperatorType::Components(bitmasks) => {
let bitmask = bitmasks.iter().map(|bm| bm.to_rinecs());
parse_quote!{
#crate_name::SystemCondition::has_any(
#( #bitmask ) | *
)
}
}
OperatorType::ComponentChanged(lit) => {
let path: Path = if let Lit::Str(lit) = lit {
syn::parse_str(&lit.value()).expect("Error parsing changed content")
}else{
panic!("Only string literal accepted as changed content")
};
parse_quote!{
#crate_name::SystemCondition::storage_changed::<#path>()
}
}
OperatorType::ResourcesChanged(lit) => {
let path: Path = if let Lit::Str(lit) = lit {
syn::parse_str(&lit.value()).expect("Error parsing resource content")
}else{
panic!("Only string literal accepted as resource content")
};
parse_quote!{
#crate_name::SystemCondition::resource_changed(#path)
}
}
OperatorType::HasResource(lit) => {
let path: Path = if let Lit::Str(lit) = lit {
syn::parse_str(&lit.value()).expect("Error parsing resource content")
}else{
panic!("Only string literal accepted as resource content")
};
parse_quote!{
#crate_name::SystemCondition::has_resource::<#path>()
}
}
}
}
}
struct Else{
pub else_token: Ident,
pub paren: Paren,
pub else_run: Lit
}
impl Parse for Else{
fn parse(input: ParseStream) -> Result<Else>{
let content;
Ok(Else{
else_token: input.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing else token"))?,
paren: parenthesized!(content in input),
else_run: content.parse()
.map_err(|err| syn::Error::new(err.span(),
"Couldn't parse else run system, expecting a string literal"))?,
})
}
}
impl Else{
fn to_rinecs(&self) -> proc_macro2::TokenStream {
let else_run_system: Path = if let Lit::Str(lit) = &self.else_run {
syn::parse_str(&lit.value()).expect("Error parsing else content")
}else{
panic!("Only string literal accepted as resource content")
};
parse_quote!{
.else_run(#else_run_system)
}
}
}
struct Condition{
pub cond_token: Ident,
pub paren: Paren,
pub condition: Operator,
}
impl Condition{
fn to_rinecs(&self) -> proc_macro2::TokenStream {
let cond = self.condition.operator.to_rinecs();
parse_quote!{
#cond
}
}
}
impl Parse for Condition{
fn parse(input: ParseStream) -> Result<Condition>{
let content;
let cond_token = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing cond token"))?;
if cond_token != Ident::new("cond", Span::call_site()){
return Err(syn::Error::new(Span::call_site(), format!("Expecting cond, got {}", cond_token)))
}
let paren = parenthesized!(content in input);
let condition = content.parse()?;
Ok(Condition{
cond_token,
paren,
condition,
})
}
}
impl Parse for Operator{
fn parse(input: ParseStream) -> Result<Operator>{
let content;
let ident = input.parse()
.map_err(|err| syn::Error::new(err.span(), "Error parsing operator ident"))?;
let ident_str = format!("{}", ident);
Ok(Operator{
paren: parenthesized!(content in input),
operator: match ident_str.as_str() {
"all" => {
let all = content.parse_terminated(Operator::parse)
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing all list: {}", err)))?;
OperatorType::All(all)
},
"any" => {
let any = content.parse_terminated(Operator::parse)
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing any list {}", err)))?;
OperatorType::Any(any)
},
"components" => {
let has = content.parse_terminated(Bitmask::parse)
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing components operator {}", err)))?;
OperatorType::Components(has)
},
"not" => {
let not = content.parse()
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing not operator {}", err)))?;
OperatorType::Not(not)
},
"changed" => {
let changed = content.parse()
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing changed operator {}", err)))?;
OperatorType::ComponentChanged(changed)
},
"resource" => {
let resource = content.parse()
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing resource operator {}", err)))?;
OperatorType::ResourcesChanged(resource)
},
"has_resource" => {
let resource = content.parse()
.map_err(|err| syn::Error::new(err.span(), format!("Error parsing has_resource operator {}", err)))?;
OperatorType::HasResource(resource)
},
_ => panic!("Wrong syntax"),
},
ident,
})
}
}
fn parse_cond_attribute(attr: &syn::Attribute) -> Condition {
let cond = attr.parse_meta().unwrap();
parse_quote!(#cond)
}
fn parse_else_attribute(attr: &syn::Attribute) -> Else {
let cond = attr.parse_meta().unwrap();
parse_quote!(#cond)
}
enum PathOrLiteral{
Path(syn::Type),
Lit(syn::LitStr)
}
impl Parse for PathOrLiteral{
fn parse(input: ParseStream) -> Result<PathOrLiteral>{
let fork = input.fork();
if fork.parse::<syn::LitStr>().is_ok() {
Ok(PathOrLiteral::Lit(input.parse()?))
}else{
Ok(PathOrLiteral::Path(input.parse()?))
}
}
}
impl ToTokens for PathOrLiteral {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
PathOrLiteral::Lit(litstr) => {
match syn::parse_str::<syn::Type>(&litstr.value()) {
Ok(ty) => tokens.append_all(ty.into_token_stream()),
Err(err) => tokens.append_all(err.to_compile_error())
}
}
PathOrLiteral::Path(path) => tokens.append_all(path.into_token_stream()),
}
}
}
trait TryExtend<A> {
fn try_extend<I>(&mut self, iter: I) -> Result<()>
where I: Iterator<Item = Result<A>>;
}
impl<T> TryExtend<T> for Punctuated<T, Comma> {
fn try_extend<I>(&mut self, iter: I) -> Result<()>
where I: Iterator<Item = Result<T>>
{
for e in iter {
self.push(e?)
}
Ok(())
}
}
fn parse_dependency_attribute<'a>(attr: &syn::Attribute, crate_name: &'a proc_macro2::TokenStream) -> Result<impl Iterator<Item = Result<syn::Expr>> + 'a> {
Ok(attr
.parse_args_with(Punctuated::<PathOrLiteral, Token![,]>::parse_terminated)?
.into_iter()
.map(move |dep| {
match dep {
PathOrLiteral::Lit(litstr) => {
let path: syn::Type = syn::parse_str(&litstr.value())?;
Ok(parse_quote!{
#crate_name::SystemId::of::<#path>()
})
}
PathOrLiteral::Path(path) => Ok(parse_quote!{
#crate_name::SystemId::of::<#path>()
})
}
}))
}
fn parse_barrier_dependency_attribute<'a>(attr: &syn::Attribute, crate_name: &'a proc_macro2::TokenStream) -> Result<impl Iterator<Item = Result<syn::Expr>> + 'a> {
Ok(attr
.parse_args_with(Punctuated::<PathOrLiteral, Token![,]>::parse_terminated)?
.into_iter()
.map(move |dep| {
match dep {
PathOrLiteral::Lit(litstr) => {
let path: syn::Type = syn::parse_str(&litstr.value())?;
Ok(parse_quote!{
#crate_name::SystemId::barrier::<#path>()
})
}
PathOrLiteral::Path(path) => Ok(parse_quote!{
#crate_name::SystemId::barrier::<#path>()
})
}
}))
}
fn parse_resource_dependency_attribute(attr: &syn::Attribute) -> Result<impl Iterator<Item = Result<syn::Expr>>> {
Ok(attr
.parse_args_with(Punctuated::<PathOrLiteral, Token![,]>::parse_terminated)
.expect("Couldn't parse resources list from attribute")
.into_iter()
.map(move |dep| {
match dep {
PathOrLiteral::Lit(litstr) => {
let path: syn::Type = syn::parse_str(&litstr.value())?;
Ok(parse_quote!{
::std::any::TypeId::of::<#path>()
})
}
PathOrLiteral::Path(path) => Ok(parse_quote!{
::std::any::TypeId::of::<#path>()
})
}
}))
}
fn parse_filter_dependency_attribute(attr: &syn::Attribute) -> Result<impl Iterator<Item = Result<syn::Type>>> {
Ok(attr
.parse_args_with(Punctuated::<PathOrLiteral, Token![,]>::parse_terminated)?
.into_iter()
.map(move |dep| {
match dep {
PathOrLiteral::Lit(litstr) => {
let path: syn::Type = syn::parse_str(&litstr.value())?;
Ok(parse_quote!{#path})
}
PathOrLiteral::Path(path) => Ok(parse_quote!{#path})
}
}))
}
fn parse_dependency_names_attribute<'a>(attr: &syn::Attribute, crate_name: &'a proc_macro2::TokenStream) -> Result<impl Iterator<Item = syn::Expr> + 'a> {
Ok(attr
.parse_args_with(Punctuated::<syn::Lit, Token![,]>::parse_terminated)?
.into_iter()
.map(move |name| parse_quote!{
#crate_name::SystemId::by_name(#name)
}))
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SystemType{
Send,
SendOnce,
ThreadLocal,
ThreadLocalOnce,
Creation,
CreationOnce,
}
pub struct SystemImpl {
desc: proc_macro2::TokenStream,
conds: proc_macro2::TokenStream,
gpu_stats: proc_macro2::TokenStream,
after: proc_macro2::TokenStream,
before: proc_macro2::TokenStream,
updates: proc_macro2::TokenStream,
needs: proc_macro2::TokenStream,
writes: proc_macro2::TokenStream,
reads: proc_macro2::TokenStream,
creates: proc_macro2::TokenStream,
}
pub fn parse_system_impl<'a>(
args: &syn::AttributeArgs,
other_attrs: &[syn::Attribute],
system_type: SystemType,
mut operators: Option<Vec<syn::Type>>
) -> Result<SystemImpl>
{
let name = Ident::new("name", Span::call_site());
let cond = Ident::new("cond", Span::call_site());
let else_run = Ident::new("else_run", Span::call_site());
let gpu_stats = Ident::new("gpu_stats", Span::call_site());
let after = Ident::new("after", Span::call_site());
let after_name = Ident::new("after_name", Span::call_site());
let before = Ident::new("before", Span::call_site());
let before_name = Ident::new("before_name", Span::call_site());
let after_barrier = Ident::new("after_barrier", Span::call_site());
let before_barrier = Ident::new("before_barrier", Span::call_site());
let needs = Ident::new("needs", Span::call_site());
let updates = Ident::new("updates", Span::call_site());
let reads = Ident::new("reads", Span::call_site());
let writes = Ident::new("writes", Span::call_site());
let creates = Ident::new("creates", Span::call_site());
let filters = Ident::new("filters", Span::call_site());
let mut system_name = None;
let mut system_conds = None;
let mut system_else = None;
let mut system_gpu_stats = false;
let mut system_after: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_before: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_updates: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_needs: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_creates: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_reads: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_writes: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut system_filters: Punctuated<syn::Type, Comma> = Punctuated::new();
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => parse_quote!(#crate_name),
Err(err) => err.to_compile_error(),
};
let system_args_as_meta = args.into_iter().map(|arg| match arg {
NestedMeta::Meta(meta) => meta.clone(),
NestedMeta::Lit(_)
=> panic!("Found literal, expected name = value or a list of conditions and else"),
});
for meta in system_args_as_meta {
match meta {
Meta::NameValue(name_value) => {
if name_value.path.is_ident(&name) {
if let Lit::Str(lit) = &name_value.lit {
system_name = Some(lit.value());
}
}
}
_ => panic!("Expecting name = \"value\"")
}
}
for attr in other_attrs {
if attr.path.is_ident(&cond) {
system_conds = Some(parse_cond_attribute(attr))
}else if attr.path.is_ident(&else_run) {
system_else = Some(parse_else_attribute(attr))
}else if attr.path.is_ident(&after) {
system_after.try_extend(parse_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&before) {
system_before.try_extend(parse_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&after_barrier) {
system_after.try_extend(parse_barrier_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&before_barrier) {
system_before.try_extend(parse_barrier_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&after_name) {
system_after.extend(parse_dependency_names_attribute(attr, &crate_name)?)
}else if attr.path.is_ident(&before_name) {
system_before.extend(parse_dependency_names_attribute(attr, &crate_name)?)
}else if attr.path.is_ident(&updates) {
system_updates.try_extend(parse_resource_dependency_attribute(attr)?)?
}else if attr.path.is_ident(&needs) {
system_needs.try_extend(parse_resource_dependency_attribute(attr)?)?
}else if system_type == SystemType::Creation && attr.path.is_ident(&creates) {
system_creates.try_extend(parse_resource_dependency_attribute(attr)?)?
}else if attr.path.is_ident(&writes) {
system_writes.try_extend(parse_resource_dependency_attribute(attr)?)?
}else if attr.path.is_ident(&reads) {
system_reads.try_extend(parse_resource_dependency_attribute(attr)?)?
}else if attr.path.is_ident(&filters) {
system_filters.try_extend(parse_filter_dependency_attribute(attr)?)?
}else if attr.path.is_ident(&gpu_stats) {
system_gpu_stats = true;
}
}
if !system_filters.is_empty() && operators.is_none() {
operators = Some(vec![])
}
for filter in system_filters {
operators.as_mut().unwrap().push(filter)
}
let conds = if let Some(system_conds) = system_conds {
let system_conds = system_conds.to_rinecs();
let else_run = if let Some(system_else) = system_else {
system_else.to_rinecs()
}else{
parse_quote!()
};
let return_ty: proc_macro2::TokenStream = match system_type{
SystemType::Send | SystemType::SendOnce => parse_quote!(#crate_name::SystemConditionSend),
SystemType::ThreadLocal | SystemType::ThreadLocalOnce => parse_quote!(#crate_name::SystemConditionThreadLocal),
SystemType::Creation | SystemType::CreationOnce => parse_quote!(#crate_name::SystemConditionCreation),
};
parse_quote!{
fn checks(world: &mut #crate_name::StorageRegistry) -> Option<#return_ty>{
Some(#system_conds #else_run .build())
}
}
}else{
if system_else.is_some(){
panic!("else without cond")
}
parse_quote!()
};
let desc = if let Some(system_name) = system_name {
parse_quote!{
fn name() -> Option<&'static str> {
Some(#system_name)
}
}
}else{
parse_quote!()
};
let gpu_stats = if system_gpu_stats {
match system_type{
SystemType::Send | SystemType::SendOnce => panic!("Send systems can't have gpu stats"),
SystemType::ThreadLocal | SystemType::ThreadLocalOnce => parse_quote!(fn runs_on_gpu() -> bool { true }),
SystemType::Creation | SystemType::CreationOnce => parse_quote!(fn runs_on_gpu() -> bool { true }),
}
}else{
parse_quote!()
};
let after = if !system_after.is_empty(){
parse_quote!{
fn after() -> Vec<#crate_name::SystemId>{
vec![ #system_after ]
}
}
}else{
parse_quote!()
};
let before = if !system_before.is_empty(){
parse_quote!{
fn before() -> Vec<#crate_name::SystemId>{
vec![ #system_before ]
}
}
}else{
parse_quote!()
};
let updates = if !system_updates.is_empty(){
parse_quote!{
fn updates() -> Vec<::std::any::TypeId>{
vec![ #system_updates ]
}
}
}else{
parse_quote!()
};
let needs = if !system_needs.is_empty(){
parse_quote!{
fn needs() -> Vec<::std::any::TypeId>{
vec![ #system_needs ]
}
}
}else{
parse_quote!()
};
let writes = if let Some(operators) = &operators {
parse_quote!{
fn writes() -> Vec<::std::any::TypeId>{
let mut writes = vec![ #system_writes ];
#(
let op_writes = <#operators as #crate_name::DataAccesses>::writes();
writes.extend_from_slice(&op_writes);
)*
writes
}
}
}else if !system_writes.is_empty(){
parse_quote!{
fn writes() -> Vec<::std::any::TypeId>{
vec![ #system_writes ]
}
}
}else{
parse_quote!()
};
let reads = if let Some(operators) = &operators {
parse_quote!{
fn reads() -> Vec<::std::any::TypeId>{
let mut reads = vec![ #system_reads ];
#(
let op_reads = <#operators as #crate_name::DataAccesses>::reads();
reads.extend_from_slice(&op_reads);
)*
reads
}
}
}else if !system_reads.is_empty(){
parse_quote!{
fn reads() -> Vec<::std::any::TypeId>{
vec![ #system_reads ]
}
}
}else{
parse_quote!()
};
let creates = if !system_creates.is_empty(){
parse_quote!{
fn creates() -> Vec<::std::any::TypeId>{
vec![ #system_creates ]
}
}
}else{
parse_quote!()
};
Ok(SystemImpl{
desc,
conds,
gpu_stats,
after,
before,
updates,
needs,
creates,
reads,
writes,
})
}
pub fn impl_any_system(
args: TokenStream,
input: TokenStream,
system_type: SystemType,
resources_ty: SystemResourcesTy) -> TokenStream
{
let args = parse_macro_input!(args as syn::AttributeArgs);
let system_impl = if let Ok(func) = syn::parse(input.clone()){
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return err.to_compile_error().into(),
};
let system_trait = match system_type{
SystemType::Send => parse_quote!(#crate_name::System),
SystemType::SendOnce => parse_quote!(#crate_name::SystemOnce),
SystemType::ThreadLocal => parse_quote!(#crate_name::SystemThreadLocal),
SystemType::ThreadLocalOnce => parse_quote!(#crate_name::SystemOnceThreadLocal),
SystemType::Creation => parse_quote!(#crate_name::CreationSystem),
SystemType::CreationOnce => parse_quote!(#crate_name::CreationSystemOnce),
};
let trait_fn_name = parse_quote!(run);
impl_any_system_fn(
&args,
&func,
system_type,
system_trait,
trait_fn_name,
parse_quote!(),
GenericsIn::Type,
resources_ty)
}else if let Ok(system_impl) = syn::parse(input){
impl_any_system_impl(&args, system_impl, system_type, resources_ty)
}else{
panic!("system attribute can only be used with an fn or a partial impl of System")
};
TokenStream::from(system_impl)
}
fn operators_from_fn_sig(sig: &syn::Signature, resources_ty: SystemResourcesTy, crate_name: &syn::Path) -> Result<Option<Vec<syn::Type>>>{
let components = if resources_ty != SystemResourcesTy::EntitiesResources {
Some(sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last().and_then(|s| {
if s.ident != "Res" && s.ident != "ResMut" && s.ident != "CreationSto" {
Some(Ok(input))
}else{
None
}
}),
syn::Type::Tuple(_) => Some(Ok(input)),
_ => Some(Err(syn::Error::new(ty.span(), "Operator argument can only be a Path or a Tuple"))),
}
_ => None,
}).collect::<Result<Vec<_>>>())
}else{
None
};
let components = components.transpose()?;
let components = components.as_ref().map(|ops| ops.iter().map(|input| match input{
syn::FnArg::Typed(ty) => match resources_ty {
SystemResourcesTy::ForEach => {
let ty = &*ty.ty;
let tokens: proc_macro2::TokenStream = parse_quote!(#ty);
syn::parse2(tokens)
},
SystemResourcesTy::Storages => {
let ty = &*ty.ty;
let tokens: proc_macro2::TokenStream = parse_quote!(<#ty as #crate_name::operators::StorageWrapper>::Data);
syn::parse2(tokens)
},
SystemResourcesTy::EntitiesResources => unreachable!(),
},
_ => Err(syn::Error::new(
input.span(),
"ForEach and Storages systems can only be used on bare functions not struct methods"
)),
}));
let resources = if resources_ty != SystemResourcesTy::EntitiesResources {
Some(sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last().and_then(|s| {
if s.ident == "Res" || s.ident == "ResMut" {
Some(Ok(input))
}else{
None
}
}),
syn::Type::Tuple(_) => None,
_ => Some(Err(syn::Error::new(ty.span(), "Operator argument can only be a Path or a Tuple"))),
}
_ => None,
}).collect::<Result<Vec<_>>>())
}else{
None
};
let resources = resources.transpose()?;
components.and_then(|components|
resources.as_ref().map(|ops| {
let resources = ops.iter().map(|input| match input{
syn::FnArg::Typed(ty) => {
let ty = &*ty.ty;
let tokens: proc_macro2::TokenStream = parse_quote!(#ty);
syn::parse2(tokens)
},
_ => Err(syn::Error::new(
input.span(),
"ForEach and Storages systems can only be used on bare functions not struct methods"
))
});
components.chain(resources).collect::<Result<_>>()
})
).transpose()
}
pub fn impl_any_system_impl(
args: &syn::AttributeArgs,
mut itemimpl: syn::ItemImpl,
system_type: SystemType,
resources_ty: SystemResourcesTy) -> proc_macro2::TokenStream
{
let operators;
if itemimpl.trait_.is_none() {
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return err.to_compile_error().into(),
};
let system_trait: Path = match system_type{
SystemType::Send => parse_quote!(#crate_name::System),
SystemType::SendOnce => parse_quote!(#crate_name::SystemOnce),
SystemType::ThreadLocal => parse_quote!(#crate_name::SystemThreadLocal),
SystemType::ThreadLocalOnce => parse_quote!(#crate_name::SystemOnceThreadLocal),
SystemType::Creation => parse_quote!(#crate_name::CreationSystem),
SystemType::CreationOnce => parse_quote!(#crate_name::CreationSystemOnce),
};
itemimpl.trait_ = Some((None, system_trait.clone(), parse_quote!(for)));
let run_method = match itemimpl.items.remove(0) {
syn::ImplItem::Method(run_method) => {
operators = match operators_from_fn_sig(&run_method.sig, resources_ty, &crate_name){
Ok(ops) => ops,
Err(err) => return err.to_compile_error(),
};
let mut sig = run_method.sig;
sig.inputs = sig.inputs.into_iter().skip(1).collect();
let trait_fn_name = &sig.ident;
let trait_fn_name = parse_quote!(#trait_fn_name);
let func = syn::ItemFn {
attrs: run_method.attrs,
vis: run_method.vis,
sig,
block: Box::new(run_method.block),
};
let trait_func = impl_trait_fn(
&func,
system_type,
trait_fn_name,
GenericsIn::Type,
resources_ty,
);
match trait_func {
Ok(trait_func) => {
syn::ImplItemMethod{
attrs: trait_func.attrs,
vis: trait_func.vis,
defaultness: run_method.defaultness,
sig: trait_func.sig,
block: *trait_func.block,
}
}
Err(err) => return err.to_compile_error(),
}
}
other=> return syn::Error::new(
other.span(),
"System implementations can only have a run method"
).to_compile_error()
};
itemimpl.items.push(syn::ImplItem::Method(run_method));
}else{
operators = None
}
match parse_system_impl(args, &itemimpl.attrs, system_type, operators) {
Ok(system) => {
if let Ok(conds) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.conds)){
itemimpl.items.push(syn::ImplItem::Method(conds));
}
if let Ok(desc) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.desc)){
itemimpl.items.push(syn::ImplItem::Method(desc));
}
if let Ok(is_gpu) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.gpu_stats)){
itemimpl.items.push(syn::ImplItem::Method(is_gpu));
}
if let Ok(before) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.before)){
itemimpl.items.push(syn::ImplItem::Method(before));
}
if let Ok(after) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.after)){
itemimpl.items.push(syn::ImplItem::Method(after));
}
if let Ok(needs) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.needs)){
itemimpl.items.push(syn::ImplItem::Method(needs));
}
if let Ok(updates) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.updates)){
itemimpl.items.push(syn::ImplItem::Method(updates));
}
if let Ok(reads) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.reads)){
itemimpl.items.push(syn::ImplItem::Method(reads));
}
if let Ok(writes) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.writes)){
itemimpl.items.push(syn::ImplItem::Method(writes));
}
if let Ok(creates) = syn::parse::<syn::ImplItemMethod>(TokenStream::from(system.creates)){
itemimpl.items.push(syn::ImplItem::Method(creates));
}
#[cfg(all(procmacro2_semver_exempt, feature="debug_systems_permissions"))]
{
let span = proc_macro2::Span::from(itemimpl.span());
let file_info = span.source_file().path().to_str().unwrap().to_string();
let line_column_info = span.start();
let file_line_info = file_info
+ ":" + &line_column_info.line.to_string()
+ ":" + &line_column_info.column.to_string();
itemimpl.items.push(syn::ImplItem::Method(parse_quote!{
fn file_line_info(&self) -> &'static str {
#file_line_info
}
}));
}
itemimpl.attrs.retain(|attr|
!attr.path.is_ident("gpu_stats")
&& !attr.path.is_ident("cond")
&& !attr.path.is_ident("else_run")
&& !attr.path.is_ident("after")
&& !attr.path.is_ident("before")
&& !attr.path.is_ident("after_name")
&& !attr.path.is_ident("before_name")
&& !attr.path.is_ident("after_barrier")
&& !attr.path.is_ident("before_barrier")
&& !attr.path.is_ident("needs")
&& !attr.path.is_ident("updates")
&& !attr.path.is_ident("reads")
&& !attr.path.is_ident("writes")
&& !attr.path.is_ident("creates")
&& !attr.path.is_ident("filters"));
parse_quote!(#itemimpl)
}
Err(err) => err.to_compile_error()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GenericsIn{
Type,
Fn,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SystemResourcesTy {
EntitiesResources,
ForEach,
Storages,
}
fn impl_trait_fn(
func: &syn::ItemFn,
system_type: SystemType,
trait_fn_name: proc_macro2::TokenStream,
generics_in: GenericsIn,
resources_ty: SystemResourcesTy,
) -> syn::Result<syn::ItemFn>
{
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return Err(err),
};
let components = if resources_ty != SystemResourcesTy::EntitiesResources {
Some(func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last().and_then(|s| {
if s.ident != "Res" && s.ident != "ResMut" && s.ident != "CreationSto" {
Some(Ok(input))
}else{
None
}
}),
syn::Type::Tuple(_) => Some(Ok(input)),
_ => Some(Err(syn::Error::new(ty.span(), "Operator argument can only be a Path or a Tuple"))),
}
_ => Some(Err(syn::Error::new(input.span(), "ForEach and Storages systems can only be used on bare functions not struct methods"))),
}).collect::<Result<Vec<_>>>())
}else{
None
}.transpose()?;
let creation_components = if resources_ty == SystemResourcesTy::Storages {
Some(func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last().and_then(|s| {
if s.ident == "CreationSto" {
Some(Ok(input))
}else{
None
}
}),
syn::Type::Tuple(_) => Some(Ok(input)),
_ => Some(Err(syn::Error::new(ty.span(), "Operator argument can only be a Path or a Tuple"))),
}
_ => Some(Err(syn::Error::new(input.span(), "ForEach and Storages systems can only be used on bare functions not struct methods"))),
}).collect::<Result<Vec<_>>>())
}else{
None
}.transpose()?;
let mut other_attrs = func.attrs.clone();
other_attrs.retain(|attr|
!attr.path.is_ident("gpu_stats")
&& !attr.path.is_ident("cond")
&& !attr.path.is_ident("else_run")
&& !attr.path.is_ident("after")
&& !attr.path.is_ident("before")
&& !attr.path.is_ident("after_name")
&& !attr.path.is_ident("before_name")
&& !attr.path.is_ident("after_barrier")
&& !attr.path.is_ident("before_barrier")
&& !attr.path.is_ident("needs")
&& !attr.path.is_ident("updates")
&& !attr.path.is_ident("reads")
&& !attr.path.is_ident("writes")
&& !attr.path.is_ident("creates")
&& !attr.path.is_ident("filters"));
let params = &func.sig.inputs;
let lifetime_generics = &func.sig.generics;
let (_, _, where_clause) = func.sig.generics.split_for_impl();
let (impl_generics, _, _) = lifetime_generics.split_for_impl();
let func_block = &func.block;
let func_output = &func.sig.output;
let self_ty: proc_macro2::TokenStream = match system_type{
SystemType::Send | SystemType::ThreadLocal | SystemType::Creation => parse_quote!(&mut self),
SystemType::SendOnce | SystemType::ThreadLocalOnce | SystemType::CreationOnce => parse_quote!(self),
};
let q = if generics_in == GenericsIn::Type{
match resources_ty {
SystemResourcesTy::EntitiesResources => {
Ok(parse_quote!{
fn #trait_fn_name (#self_ty, #params) #func_output
#func_block
})
}
SystemResourcesTy::ForEach => {
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return Err(err),
};
let query = components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &ty.ty,
_ => unreachable!("ForEach systems can only be used on bare functions not struct methods"),
}).collect::<Punctuated<_, Comma>>();
let iter_params = components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &ty.pat,
_ => unreachable!("ForEach systems can only be used on bare functions not struct methods"),
}).collect::<Punctuated<_, Comma>>();
let resource_ty = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "Res"{
match &s.arguments {
syn::PathArguments::AngleBracketed(args) => {
args.args.first().map(|arg| Ok(arg))
}
_ => Some(Err(syn::Error::new(
path.span(),
"Resources accessed with Res can only have bracketed arguemnts"
))),
}
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
}).collect::<Result<Vec<_>>>()?;
let resource_name = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "Res"{
Some(&ty.pat)
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
});
let resource_mut_ty = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "ResMut"{
match &s.arguments {
syn::PathArguments::AngleBracketed(args) => {
args.args.first().map(|arg| Ok(arg))
}
_ => Some(Err(syn::Error::new(
path.span(),
"Resources accessed with Res can only have bracketed arguemnts"
))),
}
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
}).collect::<Result<Vec<_>>>()?;
let resource_mut_name = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "ResMut"{
Some(&ty.pat)
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
});
let entities: proc_macro2::TokenStream = match system_type {
SystemType::Send | SystemType::SendOnce => parse_quote!(#crate_name::Entities),
SystemType::ThreadLocal | SystemType::ThreadLocalOnce => parse_quote!(#crate_name::EntitiesThreadLocal),
SystemType::Creation | SystemType::CreationOnce => parse_quote!(#crate_name::CreationProxy),
};
let resources: proc_macro2::TokenStream = match system_type {
SystemType::Send | SystemType::SendOnce => parse_quote!(#crate_name::Resources),
SystemType::ThreadLocal
| SystemType::ThreadLocalOnce
| SystemType::Creation
| SystemType::CreationOnce => parse_quote!(#crate_name::ResourcesThreadLocal),
};
Ok(parse_quote!{
fn #trait_fn_name (#self_ty, mut ___entities: #entities, ___resources: #resources) #func_output {
#(
let ___tmp_res = ___resources.get::<#resource_ty>().unwrap();
let #resource_name = #crate_name::Res(&*___tmp_res);
)*
#(
let mut ___tmp_res = ___resources.get_mut::<#resource_mut_ty>().unwrap();
let #resource_mut_name = #crate_name::ResMut(&mut *___tmp_res);
)*
for (#iter_params) in ___entities.iter_for_mut::<(#query)>()
#func_block
}
})
}
SystemResourcesTy::Storages => {
let storage_ty = components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &ty.ty,
_ => unreachable!("Storages systems can only be used on bare functions not struct methods"),
});
let storage_name = components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &*ty.pat,
_ => unreachable!("Storages systems can only be used on bare functions not struct methods"),
});
if SystemType::Creation != system_type
&& SystemType::CreationOnce != system_type
&& creation_components.as_ref().unwrap().iter().count() > 0
{
return Err(syn::Error::new(
creation_components.as_ref().unwrap().iter().next().unwrap().span(),
"Non creation systems can't have creation storages"
))
}
let creation_storage_ty = creation_components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &ty.ty,
_ => unreachable!("Storages systems can only be used on bare functions not struct methods"),
});
let creation_storage_name = creation_components.as_ref().unwrap().iter().map(|input| match input{
syn::FnArg::Typed(ty) => &*ty.pat,
_ => unreachable!("Storages systems can only be used on bare functions not struct methods"),
});
let resource_ty = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "Res"{
match &s.arguments {
syn::PathArguments::AngleBracketed(args) => {
args.args.first().map(|arg| Ok(arg))
}
_ => Some(Err(syn::Error::new(
path.span(),
"Resources accessed with Res can only have arguemnts between angle brackets: <_>"
))),
}
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
}).collect::<Result<Vec<_>>>()?;
let resource_name = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "Res"{
Some(&ty.pat)
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
});
let resource_mut_ty = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "ResMut"{
match &s.arguments {
syn::PathArguments::AngleBracketed(args) => {
args.args.first().map(|arg| Ok(arg))
}
_ => Some(Err(syn::Error::new(
path.span(),
"Resources accessed with Res can only have bracketed arguemnts"
))),
}
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
}).collect::<Result<Vec<_>>>()?;
let resource_mut_name = func.sig.inputs.iter().filter_map(|input| match input{
syn::FnArg::Typed(ty) => match &*ty.ty{
syn::Type::Path(path) => path.path.segments.last()
.and_then(|s| if s.ident == "ResMut"{
Some(&ty.pat)
}else{
None
}),
_ => None,
}
_ => unreachable!("ForEach and Storages systems can only be used on bare functions not struct methods"),
});
let entities: proc_macro2::TokenStream = match system_type {
SystemType::Send | SystemType::SendOnce => parse_quote!(#crate_name::Entities),
SystemType::ThreadLocal | SystemType::ThreadLocalOnce => parse_quote!(#crate_name::EntitiesThreadLocal),
SystemType::Creation | SystemType::CreationOnce => parse_quote!(#crate_name::EntitiesCreation),
};
let resources: proc_macro2::TokenStream = match system_type {
SystemType::Send | SystemType::SendOnce => parse_quote!(#crate_name::Resources),
SystemType::ThreadLocal
| SystemType::ThreadLocalOnce => parse_quote!(#crate_name::ResourcesThreadLocal),
SystemType::Creation
| SystemType::CreationOnce => parse_quote!(#crate_name::ResourcesCreation),
};
let storages: proc_macro2::TokenStream = parse_quote!{
let ___entities = ___entities.as_storages();
#(
let #storage_name = ___entities
.storage_for::<<#storage_ty as #crate_name::operators::StorageWrapper>::Data>();
)*
};
let creation_storages: proc_macro2::TokenStream = parse_quote!{
#(
let #creation_storage_name = ___entities
.creation_storage::<<#creation_storage_ty as #crate_name::operators::StorageWrapper>::Data>();
)*
};
Ok(parse_quote!{
fn #trait_fn_name (#self_ty, mut ___entities: #entities, ___resources: #resources) #func_output {
#(
let ___tmp_res = ___resources.get::<#resource_ty>().unwrap();
let #resource_name = #crate_name::Res(&*___tmp_res);
)*
#(
let mut ___tmp_res = ___resources.get_mut::<#resource_mut_ty>().unwrap();
let #resource_mut_name = #crate_name::ResMut(&mut *___tmp_res);
)*
#storages
#creation_storages
#func_block
}
})
}
}
}else{
Ok(parse_quote!{
fn #trait_fn_name #impl_generics (#self_ty, #params) #func_output #where_clause
#func_block
})
};
q
}
pub fn impl_any_system_fn(
args: &syn::AttributeArgs,
func: &syn::ItemFn,
system_type: SystemType,
system_trait: proc_macro2::TokenStream,
trait_fn_name: proc_macro2::TokenStream,
extra_methods: proc_macro2::TokenStream,
generics_in: GenericsIn,
resources_ty: SystemResourcesTy,
) -> proc_macro2::TokenStream
{
let trait_func = impl_trait_fn(
func,
system_type,
trait_fn_name,
generics_in,
resources_ty
);
let trait_func = match trait_func {
Ok(trait_func) => trait_func,
Err(err) => return err.to_compile_error()
};
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => crate_name,
Err(err) => return err.to_compile_error().into(),
};
let operators = match operators_from_fn_sig(&func.sig, resources_ty, &crate_name){
Ok(operators) => operators,
Err(err) => return err.to_compile_error(),
};
match parse_system_impl(args, &func.attrs, system_type, operators) {
Ok(system) => {
let mut other_attrs = func.attrs.clone();
other_attrs.retain(|attr|
!attr.path.is_ident("gpu_stats")
&& !attr.path.is_ident("cond")
&& !attr.path.is_ident("else_run")
&& !attr.path.is_ident("after")
&& !attr.path.is_ident("before")
&& !attr.path.is_ident("after_name")
&& !attr.path.is_ident("before_name")
&& !attr.path.is_ident("after_barrier")
&& !attr.path.is_ident("before_barrier")
&& !attr.path.is_ident("needs")
&& !attr.path.is_ident("updates")
&& !attr.path.is_ident("reads")
&& !attr.path.is_ident("writes")
&& !attr.path.is_ident("creates")
&& !attr.path.is_ident("filters"));
let system_vis = &func.vis;
let struct_name = &func.sig.ident;
let lifetime_generics = &func.sig.generics;
let (_, ty_generics, where_clause) = func.sig.generics.split_for_impl();
let (impl_generics, _, _) = lifetime_generics.split_for_impl();
let desc = system.desc;
let checks = system.conds;
let gpu_stats = system.gpu_stats;
let before = system.before;
let after = system.after;
let needs = system.needs;
let updates = system.updates;
let reads = system.reads;
let writes = system.writes;
let creates = system.creates;
let struct_decl: proc_macro2::TokenStream = if generics_in == GenericsIn::Fn || func.sig.generics.params.is_empty() {
parse_quote!{
#[allow(non_camel_case_types)]
#[derive(Default)]
#system_vis struct #struct_name;
}
}else{
let markers_decl = func.sig.generics.type_params()
.map(|p| {
let p = &p.ident;
let marker: proc_macro2::TokenStream = parse_quote!( #p: ::std::marker::PhantomData<#p>, );
marker
});
let markers = func.sig.generics.type_params()
.map(|p| {
let p = &p.ident;
let marker: proc_macro2::TokenStream = parse_quote!( #p: ::std::marker::PhantomData, );
marker
});
parse_quote!{
#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[derive(Default)]
#system_vis struct #struct_name #ty_generics #where_clause {
#( #markers_decl )*
}
impl #impl_generics #struct_name #ty_generics #where_clause {
#system_vis fn new() -> #struct_name #ty_generics {
#struct_name{
#( #markers )*
}
}
}
}
};
#[cfg(all(procmacro2_semver_exempt, feature="debug_systems_permissions"))]
let file_line_info: proc_macro2::TokenStream = {
let span = proc_macro2::Span::from(func.span());
let file_info = span.source_file().path().to_str().unwrap().to_string();
let line_column_info = span.start();
let file_line_info = file_info
+ ":" + &line_column_info.line.to_string()
+ ":" + &line_column_info.column.to_string();
parse_quote!{
fn file_line_info(&self) -> &'static str {
#file_line_info
}
}
};
#[cfg(not(all(procmacro2_semver_exempt, feature="debug_systems_permissions")))]
let file_line_info: proc_macro2::TokenStream = parse_quote!{};
let q: proc_macro2::TokenStream = if generics_in == GenericsIn::Type{
parse_quote!{
#struct_decl
impl #impl_generics #system_trait for #struct_name #ty_generics #where_clause {
#desc
#checks
#gpu_stats
#before
#after
#needs
#updates
#reads
#writes
#creates
#extra_methods
#file_line_info
#( #other_attrs )*
#trait_func
}
}
}else{
parse_quote!{
#struct_decl
impl #system_trait for #struct_name {
#desc
#checks
#gpu_stats
#before
#after
#needs
#updates
#reads
#writes
#creates
#extra_methods
#file_line_info
#( #other_attrs )*
#trait_func
}
}
};
q
}
Err(err) => err.to_compile_error()
}
}
pub fn impl_barrier(args: TokenStream, input: TokenStream) -> TokenStream
{
let args = parse_macro_input!(args as syn::AttributeArgs);
let item_struct = parse_macro_input!(input as syn::ItemStruct);
let name = Ident::new("name", Span::call_site());
let after = Ident::new("after", Span::call_site());
let after_name = Ident::new("after_name", Span::call_site());
let before = Ident::new("before", Span::call_site());
let before_name = Ident::new("before_name", Span::call_site());
let after_barrier = Ident::new("after_barrier", Span::call_site());
let before_barrier = Ident::new("before_barrier", Span::call_site());
let mut barrier_name = None;
let mut barrier_after: Punctuated<syn::Expr, Comma> = Punctuated::new();
let mut barrier_before: Punctuated<syn::Expr, Comma> = Punctuated::new();
let crate_name = match rinecs_crate_name(){
Ok(crate_name) => parse_quote!(#crate_name),
Err(err) => return err.to_compile_error().into(),
};
let system_args_as_meta = args.into_iter().map(|arg| match arg {
NestedMeta::Meta(meta) => meta.clone(),
NestedMeta::Lit(_)
=> panic!("Found literal, expected name = value or a list of conditions and else"),
});
for meta in system_args_as_meta {
match meta {
Meta::NameValue(name_value) => {
if name_value.path.is_ident(&name) {
if let Lit::Str(lit) = &name_value.lit {
barrier_name = Some(lit.value());
}
}
}
_ => panic!("Expecting name = \"value\"")
}
}
let mut process_attrs = || -> Result<()> {
for attr in &item_struct.attrs {
if attr.path.is_ident(&after) {
barrier_after.try_extend(parse_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&before) {
barrier_before.try_extend(parse_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&after_barrier) {
barrier_after.try_extend(parse_barrier_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&before_barrier) {
barrier_before.try_extend(parse_barrier_dependency_attribute(attr, &crate_name)?)?
}else if attr.path.is_ident(&after_name) {
barrier_after.extend(parse_dependency_names_attribute(attr, &crate_name)?)
}else if attr.path.is_ident(&before_name) {
barrier_before.extend(parse_dependency_names_attribute(attr, &crate_name)?)
}
}
Ok(())
};
if let Err(err) = process_attrs() {
return err.to_compile_error().into();
}
let desc: proc_macro2::TokenStream = if let Some(barrier_name) = barrier_name {
parse_quote!{
fn name() -> Option<&'static str> {
Some(#barrier_name)
}
}
}else{
parse_quote!()
};
let after: proc_macro2::TokenStream = if !barrier_after.is_empty(){
parse_quote!{
fn after() -> Vec<#crate_name::SystemId>{
vec![ #barrier_after ]
}
}
}else{
parse_quote!()
};
let before: proc_macro2::TokenStream = if !barrier_before.is_empty(){
parse_quote!{
fn before() -> Vec<#crate_name::SystemId>{
vec![ #barrier_before ]
}
}
}else{
parse_quote!()
};
let struct_name = &item_struct.ident;
let (impl_generics, ty_generics, where_clause) = item_struct.generics.split_for_impl();
let item_struct = &item_struct;
let barrier_impl: proc_macro2::TokenStream = parse_quote!{
#item_struct
impl #impl_generics #crate_name::Barrier for #struct_name #ty_generics #where_clause {
#desc
#after
#before
}
};
TokenStream::from(barrier_impl)
}