engine/transaction_derive/src/apply_transaction.rs

187 lines
6.1 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::Ident;
use crate::attribute_info::*;
use crate::data_type::*;
use crate::transaction_field::*;
pub struct ApplyTransaction<'a> {
pub variable_name: &'a Ident,
pub data_type: &'a DataType,
pub attribute_infos: &'a Vec<AttributeInfo>,
pub tracker: &'a Option<Ident>,
}
impl<'a> From<&'a TransactionField> for ApplyTransaction<'a> {
fn from(tf: &'a TransactionField) -> Self {
Self {
variable_name: &tf.variable_name,
data_type: &tf.data_type,
attribute_infos: &tf.attribute_infos,
tracker: &tf.tracker,
}
}
}
fn from_str_quote(
convert_type: &Option<&TokenStream>,
custom_into: &Option<&Ident>,
target_type: &TokenStream,
) -> TokenStream {
match (convert_type, custom_into) {
(Some(convert_type), Some(custom_into)) => {
quote! {
use ron::from_str;
use serde::Deserialize;
let p: Option<#convert_type> = from_str(value).ok();
let f: Option<#target_type> = p.map(|p| self.#custom_into(p));
}
}
(Some(convert_type), None) => {
quote! {
use ron::from_str;
use serde::Deserialize;
let p: Option<#convert_type> = from_str(value).ok();
let f: Option<#target_type> = p.map(|p| p.into());
}
}
(None, _) => {
quote! {
use ron::from_str;
use serde::Deserialize;
let f = from_str(value).ok();
}
}
}
}
impl<'a> ToTokens for ApplyTransaction<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let var = &self.variable_name;
let variable_string = var.to_string();
let variable_str = variable_string.as_str();
let data_type = &self.data_type;
let parsed_variable = Ident::new("value", Span::call_site());
let time_variable = Ident::new("time", Span::call_site());
let last_change = format_ident!("{}_last_change", var);
let tracker = self.tracker.as_ref().expect("tracker not set");
let convert_type = AttributeInfo::find_convert_type(&self.attribute_infos);
let custom_into = AttributeInfo::find_custom_into(&self.attribute_infos);
let exec_function = AttributeInfo::find_apply_exec_function(&self.attribute_infos);
let index_value = quote! {
let pos = #parsed_variable
.find('!')
.ok_or(anyhow::Error::msg(format!(
"Failed parsing in ApplyTransaction '!'. Found {}",
stringify!(#parsed_variable)
)))?;
let (index, value) = #parsed_variable.split_at(pos);
let value = value.trim_start_matches('!');
let index: usize = index.parse()?;
};
let (set_value_indexed, set_value) = match exec_function {
Some(exec_function) => (
quote! {
if let Some(f) = f {
let old_value = std::mem::replace(&mut self.#var[index], f);
self.#exec_function(old_value, index, multi_mut, scene)?;
}
},
quote! {
if let Some(f) = f {
let old_value = std::mem::replace(&mut self.#var, f);
self.#exec_function(old_value, multi_mut, scene)?;
}
},
),
None => (
quote! {
if let Some(f) = f {
self.#var[index] = f;
}
},
quote! {
if let Some(f) = f {
self.#var = f;
}
},
),
};
// check features
AttributeInfo::set_features(&self.attribute_infos, tokens);
proc_macro2::TokenStream::from(match data_type {
DataType::Array(array) => {
let from_str = from_str_quote(&convert_type, &custom_into, &array.inner_type);
quote! {
#variable_str => {
#index_value
if #time_variable > self.#tracker.#last_change[index] {
self.#tracker.#last_change[index] = #time_variable;
#from_str
#set_value_indexed
}
}
}
}
DataType::Vector(vector) => {
let from_str = from_str_quote(&convert_type, &custom_into, &vector.inner_type);
quote! {
#variable_str => {
#index_value
if self.#tracker.#last_change.len() as i32 - 1 < index as i32 {
let size_difference = index as i32 - (self.#tracker.#last_change.len() as i32 - 1);
for _ in 0..size_difference {
self.#tracker.#last_change.push(std::time::Duration::default());
}
}
if #time_variable > self.#tracker.#last_change[index] {
self.#tracker.#last_change[index] = #time_variable;
#from_str
#set_value_indexed
}
}
}
}
DataType::Other(ts) => {
let from_str = from_str_quote(&convert_type, &custom_into, &ts);
quote! {
#variable_str => {
if #time_variable > self.#tracker.#last_change {
self.#tracker.#last_change = #time_variable;
let value = #parsed_variable;
#from_str
#set_value
}
}
}
}
})
.to_tokens(tokens);
}
}