vapabi-derive 9.0.1

Easy to use conversion of vapory contract calls to bytecode.
Documentation
use vapabi;
use proc_macro2::TokenStream;

use super::{
	input_names, template_param_type, rust_type, get_template_names, to_token, from_template_param,
	to_vapabi_param_vec,
};

/// Structure used to generate contract's constructor interface.
pub struct Constructor {
	inputs_declarations: Vec<TokenStream>,
	inputs_definitions: Vec<TokenStream>,
	tokenize: Vec<TokenStream>,
	recreate_inputs: TokenStream,
}

impl<'a> From<&'a vapabi::Constructor> for Constructor {
	fn from(c: &'a vapabi::Constructor) -> Self {
		// [param0, hello_world, param2]
		let input_names = input_names(&c.inputs);

		// [T0: Into<Uint>, T1: Into<Bytes>, T2: IntoIterator<Item = U2>, U2 = Into<Uint>]
		let inputs_declarations = c.inputs.iter().enumerate()
			.map(|(index, param)| template_param_type(&param.kind, index))
			.collect();

		// [Uint, Bytes, Vec<Uint>]
		let kinds: Vec<_> = c.inputs
			.iter()
			.map(|param| rust_type(&param.kind))
			.collect();

		// [T0, T1, T2]
		let template_names: Vec<_> = get_template_names(&kinds);

		// [param0: T0, hello_world: T1, param2: T2]
		let inputs_definitions = input_names.iter().zip(template_names.iter())
			.map(|(param_name, template_name)| quote! { #param_name: #template_name });

		let inputs_definitions = Some(quote! { code: vapabi::Bytes }).into_iter()
			.chain(inputs_definitions)
			.collect();

		// [Token::Uint(param0.into()), Token::Bytes(hello_world.into()), Token::Array(param2.into_iter().map(Into::into).collect())]
		let tokenize: Vec<_> = input_names.iter().zip(c.inputs.iter())
			.map(|(param_name, param)| to_token(&from_template_param(&param.kind, &param_name), &param.kind))
			.collect();

		Constructor {
			inputs_declarations,
			inputs_definitions,
			tokenize,
			recreate_inputs: to_vapabi_param_vec(&c.inputs),
		}
	}
}

impl Constructor {
	/// Generates contract constructor interface.
	pub fn generate(&self) -> TokenStream {
		let declarations = &self.inputs_declarations;
		let definitions = &self.inputs_definitions;
		let tokenize = &self.tokenize;
		let recreate_inputs = &self.recreate_inputs;

		quote! {
			/// Encodes a call to contract's constructor.
			pub fn constructor<#(#declarations),*>(#(#definitions),*) -> vapabi::Bytes {
				let c = vapabi::Constructor {
					inputs: #recreate_inputs,
				};
				let tokens = vec![#(#tokenize),*];
				c.encode_input(code, &tokens).expect(INTERNAL_ERR)
			}
		}
	}
}

#[cfg(test)]
mod tests {
	use vapabi;
	use super::Constructor;

	#[test]
	fn test_no_params() {
		let vapabi_constructor = vapabi::Constructor {
			inputs: vec![],
		};

		let c = Constructor::from(&vapabi_constructor);

		let expected = quote! {
			/// Encodes a call to contract's constructor.
			pub fn constructor<>(code: vapabi::Bytes) -> vapabi::Bytes {
				let c = vapabi::Constructor {
					inputs: vec![],
				};
				let tokens = vec![];
				c.encode_input(code, &tokens).expect(INTERNAL_ERR)
			}
		};

		assert_eq!(expected.to_string(), c.generate().to_string());
	}

	#[test]
	fn test_one_param() {
		let vapabi_constructor = vapabi::Constructor {
			inputs: vec![
				vapabi::Param {
					name: "foo".into(),
					kind: vapabi::ParamType::Uint(256),
				}
			],
		};

		let c = Constructor::from(&vapabi_constructor);

		let expected = quote! {
			/// Encodes a call to contract's constructor.
			pub fn constructor<T0: Into<vapabi::Uint> >(code: vapabi::Bytes, foo: T0) -> vapabi::Bytes {
				let c = vapabi::Constructor {
					inputs: vec![vapabi::Param {
						name: "foo".to_owned(),
						kind: vapabi::ParamType::Uint(256usize)
					}],
				};
				let tokens = vec![vapabi::Token::Uint(foo.into())];
				c.encode_input(code, &tokens).expect(INTERNAL_ERR)
			}
		};

		assert_eq!(expected.to_string(), c.generate().to_string());
	}
}