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, pub tracker: &'a Option, } 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); } }