use {syn, vapabi};
use heck::SnakeCase;
use proc_macro2::TokenStream;
use syn::export::Span;
use super::{
input_names, template_param_type, rust_type, get_template_names, from_template_param, to_token,
to_vapabi_param_vec, get_output_kinds, from_token
};
struct TemplateParam {
declaration: TokenStream,
definition: TokenStream,
}
struct Inputs {
tokenize: Vec<TokenStream>,
template_params: Vec<TemplateParam>,
recreate_quote: TokenStream,
}
struct Outputs {
implementation: TokenStream,
result: TokenStream,
recreate_quote: TokenStream,
}
pub struct Function {
name: String,
inputs: Inputs,
outputs: Outputs,
constant: bool,
}
impl<'a> From<&'a vapabi::Function> for Function {
fn from(f: &'a vapabi::Function) -> Self {
let input_names = input_names(&f.inputs);
let declarations = f.inputs.iter().enumerate()
.map(|(index, param)| template_param_type(¶m.kind, index));
let kinds: Vec<_> = f.inputs
.iter()
.map(|param| rust_type(¶m.kind))
.collect();
let template_names: Vec<_> = get_template_names(&kinds);
let definitions = input_names.iter().zip(template_names.iter())
.map(|(param_name, template_name)| quote! { #param_name: #template_name });
let template_params = declarations.zip(definitions)
.map(|(declaration, definition)| TemplateParam { declaration, definition })
.collect();
let tokenize: Vec<_> = input_names.iter().zip(f.inputs.iter())
.map(|(param_name, param)| to_token(&from_template_param(¶m.kind, ¶m_name), ¶m.kind))
.collect();
let output_result = get_output_kinds(&f.outputs);
let output_implementation = match f.outputs.len() {
0 => quote! {
let _output = output;
Ok(())
},
1 => {
let o = quote! { out };
let from_first = from_token(&f.outputs[0].kind, &o);
quote! {
let out = self.0.decode_output(output)?.into_iter().next().expect(INTERNAL_ERR);
Ok(#from_first)
}
},
_ => {
let o = quote! { out.next().expect(INTERNAL_ERR) };
let outs: Vec<_> = f.outputs
.iter()
.map(|param| from_token(¶m.kind, &o))
.collect();
quote! {
let mut out = self.0.decode_output(output)?.into_iter();
Ok(( #(#outs),* ))
}
},
};
Function {
name: f.name.clone(),
inputs: Inputs {
tokenize,
template_params,
recreate_quote: to_vapabi_param_vec(&f.inputs),
},
outputs: Outputs {
implementation: output_implementation,
result: output_result,
recreate_quote: to_vapabi_param_vec(&f.outputs),
},
constant: f.constant,
}
}
}
impl Function {
pub fn generate(&self) -> TokenStream {
let name = &self.name;
let module_name = syn::Ident::new(&self.name.to_snake_case(), Span::call_site());
let tokenize = &self.inputs.tokenize;
let declarations: &Vec<_> = &self.inputs.template_params.iter().map(|i| &i.declaration).collect();
let definitions: &Vec<_> = &self.inputs.template_params.iter().map(|i| &i.definition).collect();
let recreate_inputs = &self.inputs.recreate_quote;
let recreate_outputs = &self.outputs.recreate_quote;
let constant = &self.constant;
let outputs_result = &self.outputs.result;
let outputs_implementation = &self.outputs.implementation;
quote! {
pub mod #module_name {
use vapabi;
use super::INTERNAL_ERR;
fn function() -> vapabi::Function {
vapabi::Function {
name: #name.into(),
inputs: #recreate_inputs,
outputs: #recreate_outputs,
constant: #constant,
}
}
pub struct Decoder(vapabi::Function);
impl vapabi::FunctionOutputDecoder for Decoder {
type Output = #outputs_result;
fn decode(&self, output: &[u8]) -> vapabi::Result<Self::Output> {
#outputs_implementation
}
}
pub fn encode_input<#(#declarations),*>(#(#definitions),*) -> vapabi::Bytes {
let f = function();
let tokens = vec![#(#tokenize),*];
f.encode_input(&tokens).expect(INTERNAL_ERR)
}
pub fn decode_output(output: &[u8]) -> vapabi::Result<#outputs_result> {
vapabi::FunctionOutputDecoder::decode(&Decoder(function()), output)
}
pub fn call<#(#declarations),*>(#(#definitions),*) -> (vapabi::Bytes, Decoder) {
let f = function();
let tokens = vec![#(#tokenize),*];
(f.encode_input(&tokens).expect(INTERNAL_ERR), Decoder(f))
}
}
}
}
}
#[cfg(test)]
mod tests {
use vapabi;
use super::Function;
#[test]
fn test_no_params() {
let vapabi_function = vapabi::Function {
name: "empty".into(),
inputs: vec![],
outputs: vec![],
constant: false,
};
let f = Function::from(&vapabi_function);
let expected = quote! {
pub mod empty {
use vapabi;
use super::INTERNAL_ERR;
fn function() -> vapabi::Function {
vapabi::Function {
name: "empty".into(),
inputs: vec![],
outputs: vec![],
constant: false,
}
}
pub struct Decoder(vapabi::Function);
impl vapabi::FunctionOutputDecoder for Decoder {
type Output = ();
fn decode(&self, output: &[u8]) -> vapabi::Result<Self::Output> {
let _output = output;
Ok(())
}
}
pub fn encode_input<>() -> vapabi::Bytes {
let f = function();
let tokens = vec![];
f.encode_input(&tokens).expect(INTERNAL_ERR)
}
pub fn decode_output(output: &[u8]) -> vapabi::Result<()> {
vapabi::FunctionOutputDecoder::decode(&Decoder(function()), output)
}
pub fn call<>() -> (vapabi::Bytes, Decoder) {
let f = function();
let tokens = vec![];
(f.encode_input(&tokens).expect(INTERNAL_ERR), Decoder(f))
}
}
};
assert_eq!(expected.to_string(), f.generate().to_string());
}
#[test]
fn test_one_param() {
let vapabi_function = vapabi::Function {
name: "hello".into(),
inputs: vec![
vapabi::Param {
name: "foo".into(),
kind: vapabi::ParamType::Address,
}
],
outputs: vec![
vapabi::Param {
name: "bar".into(),
kind: vapabi::ParamType::Uint(256),
}
],
constant: false,
};
let f = Function::from(&vapabi_function);
let expected = quote! {
pub mod hello {
use vapabi;
use super::INTERNAL_ERR;
fn function() -> vapabi::Function {
vapabi::Function {
name: "hello".into(),
inputs: vec![vapabi::Param {
name: "foo".to_owned(),
kind: vapabi::ParamType::Address
}],
outputs: vec![vapabi::Param {
name: "bar".to_owned(),
kind: vapabi::ParamType::Uint(256usize)
}],
constant: false,
}
}
pub struct Decoder(vapabi::Function);
impl vapabi::FunctionOutputDecoder for Decoder {
type Output = vapabi::Uint;
fn decode(&self, output: &[u8]) -> vapabi::Result<Self::Output> {
let out = self.0.decode_output(output)?.into_iter().next().expect(INTERNAL_ERR);
Ok(out.to_uint().expect(INTERNAL_ERR))
}
}
pub fn encode_input<T0: Into<vapabi::Address> >(foo: T0) -> vapabi::Bytes {
let f = function();
let tokens = vec![vapabi::Token::Address(foo.into())];
f.encode_input(&tokens).expect(INTERNAL_ERR)
}
pub fn decode_output(output: &[u8]) -> vapabi::Result<vapabi::Uint> {
vapabi::FunctionOutputDecoder::decode(&Decoder(function()), output)
}
pub fn call<T0: Into<vapabi::Address> >(foo: T0) -> (vapabi::Bytes, Decoder) {
let f = function();
let tokens = vec![vapabi::Token::Address(foo.into())];
(f.encode_input(&tokens).expect(INTERNAL_ERR), Decoder(f))
}
}
};
assert_eq!(expected.to_string(), f.generate().to_string());
}
#[test]
fn test_multiple_params() {
let vapabi_function = vapabi::Function {
name: "multi".into(),
inputs: vec![
vapabi::Param {
name: "foo".into(),
kind: vapabi::ParamType::FixedArray(Box::new(vapabi::ParamType::Address), 2),
},
vapabi::Param {
name: "bar".into(),
kind: vapabi::ParamType::Array(Box::new(vapabi::ParamType::Uint(256))),
}
],
outputs: vec![
vapabi::Param {
name: "".into(),
kind: vapabi::ParamType::Uint(256),
},
vapabi::Param {
name: "".into(),
kind: vapabi::ParamType::String,
}
],
constant: false,
};
let f = Function::from(&vapabi_function);
let expected = quote! {
pub mod multi {
use vapabi;
use super::INTERNAL_ERR;
fn function() -> vapabi::Function {
vapabi::Function {
name: "multi".into(),
inputs: vec![vapabi::Param {
name: "foo".to_owned(),
kind: vapabi::ParamType::FixedArray(Box::new(vapabi::ParamType::Address), 2usize)
}, vapabi::Param {
name: "bar".to_owned(),
kind: vapabi::ParamType::Array(Box::new(vapabi::ParamType::Uint(256usize)))
}],
outputs: vec![vapabi::Param {
name: "".to_owned(),
kind: vapabi::ParamType::Uint(256usize)
}, vapabi::Param {
name: "".to_owned(),
kind: vapabi::ParamType::String
}],
constant: false,
}
}
pub struct Decoder(vapabi::Function);
impl vapabi::FunctionOutputDecoder for Decoder {
type Output = (vapabi::Uint, String);
fn decode(&self, output: &[u8]) -> vapabi::Result<Self::Output> {
let mut out = self.0.decode_output(output)?.into_iter();
Ok((out.next().expect(INTERNAL_ERR).to_uint().expect(INTERNAL_ERR), out.next().expect(INTERNAL_ERR).to_string().expect(INTERNAL_ERR)))
}
}
pub fn encode_input<T0: Into<[U0; 2usize]>, U0: Into<vapabi::Address>, T1: IntoIterator<Item = U1>, U1: Into<vapabi::Uint> >(foo: T0, bar: T1) -> vapabi::Bytes {
let f = function();
let tokens = vec![{
let v = (Box::new(foo.into()) as Box<[_]>).into_vec().into_iter().map(Into::into).collect::<Vec<_>>().into_iter().map(|inner| vapabi::Token::Address(inner)).collect();
vapabi::Token::FixedArray(v)
}, {
let v = bar.into_iter().map(Into::into).collect::<Vec<_>>().into_iter().map(|inner| vapabi::Token::Uint(inner)).collect();
vapabi::Token::Array(v)
}];
f.encode_input(&tokens).expect(INTERNAL_ERR)
}
pub fn decode_output(output: &[u8]) -> vapabi::Result<(vapabi::Uint, String)> {
vapabi::FunctionOutputDecoder::decode(&Decoder(function()), output)
}
pub fn call<T0: Into<[U0; 2usize]>, U0: Into<vapabi::Address>, T1: IntoIterator<Item = U1>, U1: Into<vapabi::Uint> >(foo: T0, bar: T1) -> (vapabi::Bytes, Decoder) {
let f = function();
let tokens = vec![{
let v = (Box::new(foo.into()) as Box<[_]>).into_vec().into_iter().map(Into::into).collect::<Vec<_>>().into_iter().map(|inner| vapabi::Token::Address(inner)).collect();
vapabi::Token::FixedArray(v)
}, {
let v = bar.into_iter().map(Into::into).collect::<Vec<_>>().into_iter().map(|inner| vapabi::Token::Uint(inner)).collect();
vapabi::Token::Array(v)
}];
(f.encode_input(&tokens).expect(INTERNAL_ERR), Decoder(f))
}
}
};
assert_eq!(expected.to_string(), f.generate().to_string());
}
}