187 lines
6.1 KiB
Rust
187 lines
6.1 KiB
Rust
|
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);
|
||
|
}
|
||
|
}
|