#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
use core::ops::Index;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use std::str::FromStr;
use syn::Lit;
use syn::{parse_macro_input, AttributeArgs, ItemFn, Meta, NestedMeta};
#[proc_macro_attribute]
pub fn compile_if(args: TokenStream, input: TokenStream) -> TokenStream {
let _args = parse_macro_input!(args as AttributeArgs);
let input = parse_macro_input!(input as ItemFn);
if input.sig.inputs.len() != 2 {
panic!("Too may Arguments to function");
}
let context_arg = input.sig.inputs.index(1);
let name = input.sig.ident;
let compile_if_name = format_ident!("compile_if_{}", name);
let block = input.block;
proc_macro::TokenStream::from(quote! {
fn #compile_if_name(&self, #context_arg) -> sapio::contract::actions::ConditionalCompileType
#block
fn #name() -> Option<sapio::contract::actions::ConditionallyCompileIf<Self>> {
Some(sapio::contract::actions::ConditionallyCompileIf::Fresh(Self::#compile_if_name))
}
})
}
#[proc_macro_attribute]
pub fn guard(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let input = parse_macro_input!(input as ItemFn);
if input.sig.inputs.len() != 2 {
panic!("Too may Arguments to function");
}
let context_arg = input.sig.inputs.index(1);
let name = input.sig.ident;
let guard_name = format_ident!("guard_{}", name);
let block = input.block;
let mut ty = format_ident!("Fresh");
let simp_gen_f = simp_at(&args).unwrap_or(TokenStream::from_str("None").unwrap().into());
for arg in args {
match arg {
NestedMeta::Meta(Meta::NameValue(v)) if v.path.is_ident("cached") => {
ty = format_ident!("Cached");
}
_ => {}
}
}
proc_macro::TokenStream::from(quote! {
fn #guard_name(&self, #context_arg) -> sapio::sapio_base::Clause
#block
fn #name() -> Option<sapio::contract::actions::Guard<Self>> {
Some(sapio::contract::actions::Guard::#ty(Self::#guard_name, #simp_gen_f))
}
})
}
fn get_arrays(args: &Vec<NestedMeta>) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
let mut compile_if_array = None;
let mut guarded_by_array = None;
for arg in args {
match (&compile_if_array, &guarded_by_array, arg) {
(_, None, NestedMeta::Meta(Meta::NameValue(v))) if v.path.is_ident("guarded_by") => {
match &v.lit {
Lit::Str(l) => {
guarded_by_array = Some(l.parse().expect("Token Stream Parsing"));
}
_ => panic!("Improperly Formatted {:?}", v),
}
}
(_, Some(_), NestedMeta::Meta(Meta::NameValue(v))) if v.path.is_ident("guarded_by") => {
panic!("Repeated guarded_by arguments");
}
(None, _, NestedMeta::Meta(Meta::NameValue(v))) if v.path.is_ident("compile_if") => {
match &v.lit {
Lit::Str(l) => {
compile_if_array = Some(l.parse().expect("Token Stream Parsing"))
}
_ => panic!("Improperly Formatted {:?}", v),
}
}
(Some(_), _, NestedMeta::Meta(Meta::NameValue(v))) if v.path.is_ident("compile_if") => {
panic!("Repeated compile_if arguments");
}
_v => {}
}
}
(
compile_if_array.unwrap_or(quote! {[]}),
guarded_by_array.unwrap_or(quote! {[]}),
)
}
#[proc_macro_attribute]
pub fn then(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let input = parse_macro_input!(input as ItemFn);
if input.sig.inputs.len() != 2 {
panic!("Too may Arguments to function");
}
let context_arg = input.sig.inputs.index(1);
let name = input.sig.ident;
let then_fn_name = format_ident!("then_{}", name);
let block = input.block;
let (cia, gba) = get_arrays(&args);
proc_macro::TokenStream::from(quote! {
fn #name<'a>() -> Option<sapio::contract::actions::ThenFuncAsFinishOrFunc<'a, Self, <Self as sapio::contract::Contract>::StatefulArguments>>{
Some(sapio::contract::actions::ThenFunc{
guard: &#gba,
conditional_compile_if: &#cia,
func: Self::#then_fn_name,
name: std::sync::Arc::new(std::stringify!(#name).into()),
}.into())
}
fn #then_fn_name(&self, #context_arg, _: sapio::contract::actions::ThenFuncTypeTag) -> sapio::contract::TxTmplIt
#block
})
}
fn web_api(args: &Vec<NestedMeta>) -> proc_macro2::TokenStream {
for arg in args {
match arg {
NestedMeta::Meta(Meta::Path(v)) if v.is_ident("web_api") => {
return quote! { sapio::contract::actions::WebAPIEnabled};
}
_ => continue,
}
}
quote! { sapio::contract::actions::WebAPIDisabled}
}
fn coerce_args(args: &Vec<NestedMeta>) -> proc_macro2::TokenStream {
for arg in args {
match arg {
NestedMeta::Meta(Meta::NameValue(v)) if v.path.is_ident("coerce_args") => {
match &v.lit {
Lit::Str(l) => {
return l.parse().expect("Token Stream Parsing");
}
_ => panic!("Improperly Formatted {:?}", v),
}
}
_ => continue,
}
}
panic!("No Coerce Arguments found");
}
fn simp_at(args: &Vec<NestedMeta>) -> Option<proc_macro2::TokenStream> {
for arg in args {
match arg {
NestedMeta::Meta(Meta::NameValue(v)) if v.path.is_ident("simps") => match &v.lit {
Lit::Str(l) => {
return Some(l.parse().expect("Token Stream Parsing"));
}
_ => panic!("Improperly Formatted {:?}", v),
},
_ => continue,
}
}
None
}
fn web_api_schema(
args: &Vec<NestedMeta>,
name: &syn::Ident,
typ: &syn::FnArg,
) -> proc_macro2::TokenStream {
if let syn::FnArg::Typed(v) = typ {
let ty = &v.ty;
for arg in args {
match arg {
NestedMeta::Meta(Meta::Path(v)) if v.is_ident("web_api") => {
return quote! {
const #name : Option<&'static dyn Fn() -> std::sync::Arc<serde_json::Value>> =
Some(&|| sapio::contract::macros::get_schema_for::<#ty>());
};
}
_ => continue,
}
}
} else {
panic!("Wrong type: {:?}", typ);
}
quote! {
const #name : Option<&'static dyn Fn() -> std::sync::Arc<serde_json::Value>> = None;
}
}
#[proc_macro_attribute]
pub fn continuation(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let input = parse_macro_input!(input as ItemFn);
let name = input.sig.ident;
let continue_name = format_ident!("continue_{}", name);
let block = input.block;
if input.sig.inputs.len() != 3 {
panic!("Too may Arguments to function");
}
let arg_type = input
.sig
.inputs
.last()
.expect("Must have at least one argument");
let context_arg = input.sig.inputs.index(1);
let (cia, gba) = get_arrays(&args);
let web_api_type = web_api(&args);
let continue_schema_for_name =
format_ident!("CONTINUE_SCHEMA_FOR_{}", name.to_string().to_uppercase());
let web_api_schema_s = web_api_schema(&args, &continue_schema_for_name, arg_type);
let coerce_args_f = coerce_args(&args);
let simp_gen_f = simp_at(&args).unwrap_or(TokenStream::from_str("None").unwrap().into());
proc_macro::TokenStream::from(quote! {
#web_api_schema_s
fn #continue_name(&self, #context_arg, #arg_type) -> sapio::contract::TxTmplIt
#block
fn #name<'a>() -> Option<Box<dyn
sapio::contract::actions::CallableAsFoF<Self, <Self as sapio::contract::Contract>::StatefulArguments>>>
{
let f : sapio::contract::actions::FinishOrFunc<_, _, _, #web_api_type>= sapio::contract::actions::FinishOrFunc{
simp_gen: #simp_gen_f,
coerce_args: #coerce_args_f,
guard: &#gba,
conditional_compile_if: &#cia,
func: Self::#continue_name,
schema: Self::#continue_schema_for_name.map(|f|f()),
name: std::sync::Arc::new(std::stringify!(#name).into()),
f: std::default::Default::default(),
returned_txtmpls_modify_guards: false,
extract_clause_from_txtmpl: sapio::contract::actions::default_extract_clause_from_txtmpl
};
Some(Box::new(f))
}
})
}