diff --git a/example/src/main.rs b/example/src/main.rs index 1612a22..fc7c3a3 100644 --- a/example/src/main.rs +++ b/example/src/main.rs @@ -25,83 +25,62 @@ rest_resource!{Users, route => { route.update::(); }} -#[derive(Deserialize, OpenapiType, Serialize)] -struct TestStruct -{ - foo : String -} - #[derive(Deserialize, OpenapiType, Serialize)] struct User { - username : String, - test : Option> + username : String } -impl ResourceReadAll>>> for Users +#[rest_read_all(Users)] +fn read_all(_state : &mut State) -> Success>> { - fn read_all(_state : &mut State) -> Success>> - { - vec![Username().fake(), Username().fake()] - .into_iter() - .map(|username| Some(User { username, test: None })) - .collect::>>() - .into() - } + vec![Username().fake(), Username().fake()] + .into_iter() + .map(|username| Some(User { username })) + .collect::>>() + .into() } -impl ResourceRead> for Users +#[rest_read(Users)] +fn read(_state : &mut State, id : u64) -> Success { - fn read(_state : &mut State, id : u64) -> Success - { - let username : String = Username().fake(); - User { username: format!("{}{}", username, id), test: None }.into() - } + let username : String = Username().fake(); + User { username: format!("{}{}", username, id) }.into() } -impl ResourceCreate> for Users +#[rest_create(Users)] +fn create(_state : &mut State, body : User) -> Success<()> { - fn create(_state : &mut State, body : User) -> Success<()> - { - info!("Created User: {}", body.username); - ().into() - } + info!("Created User: {}", body.username); + ().into() } -impl ResourceUpdateAll, Success<()>> for Users +#[rest_update_all(Users)] +fn update_all(_state : &mut State, body : Vec) -> Success<()> { - fn update_all(_state : &mut State, body : Vec) -> Success<()> - { - info!("Changing all Users to {:?}", body.into_iter().map(|u| u.username).collect::>()); - ().into() - } + info!("Changing all Users to {:?}", body.into_iter().map(|u| u.username).collect::>()); + ().into() } -impl ResourceUpdate> for Users +#[rest_update(Users)] +fn update(_state : &mut State, id : u64, body : User) -> Success<()> { - fn update(_state : &mut State, id : u64, body : User) -> Success<()> - { - info!("Change User {} to {}", id, body.username); - ().into() - } + info!("Change User {} to {}", id, body.username); + ().into() } -impl ResourceDeleteAll> for Users +#[rest_delete_all(Users)] +fn delete_all(_state : &mut State) -> Success<()> { - fn delete_all(_state : &mut State) -> Success<()> - { - info!("Delete all Users"); - ().into() - } + info!("Delete all Users"); + ().into() } -impl ResourceDelete> for Users +#[rest_delete(Users)] +fn delete(_state : &mut State, id : u64) -> Success<()> { - fn delete(_state : &mut State, id : u64) -> Success<()> - { - info!("Delete User {}", id); - ().into() - } + info!("Delete User {}", id); + ().into() } const ADDR : &str = "127.0.0.1:18080"; diff --git a/gotham_restful_derive/src/lib.rs b/gotham_restful_derive/src/lib.rs index 3cddc5c..b6477f6 100644 --- a/gotham_restful_derive/src/lib.rs +++ b/gotham_restful_derive/src/lib.rs @@ -2,6 +2,8 @@ extern crate proc_macro; use proc_macro::TokenStream; +mod method; +use method::{expand_method, Method}; #[cfg(feature = "openapi")] mod openapi_type; @@ -11,3 +13,52 @@ pub fn derive_openapi_type(tokens : TokenStream) -> TokenStream { openapi_type::expand(tokens) } + +#[proc_macro_attribute] +pub fn rest_read_all(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::ReadAll, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_read(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::Read, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_create(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::Create, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_update_all(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::UpdateAll, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_update(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::Update, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_delete_all(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::DeleteAll, attr, item); + output +} + +#[proc_macro_attribute] +pub fn rest_delete(attr : TokenStream, item : TokenStream) -> TokenStream +{ + let output = expand_method(Method::Delete, attr, item); + output +} diff --git a/gotham_restful_derive/src/method.rs b/gotham_restful_derive/src/method.rs new file mode 100644 index 0000000..53fb66d --- /dev/null +++ b/gotham_restful_derive/src/method.rs @@ -0,0 +1,96 @@ +use proc_macro::TokenStream; +use proc_macro2::{Ident, TokenStream as TokenStream2}; +use quote::{format_ident, quote}; +use syn::{ + FnArg, + ItemFn, + ReturnType, + parse_macro_input +}; + +pub enum Method +{ + ReadAll, + Read, + Create, + UpdateAll, + Update, + DeleteAll, + Delete +} + +impl Method +{ + fn trait_ident(&self) -> Ident + { + use Method::*; + + let name = match self { + ReadAll => "ReadAll", + Read => "Read", + Create => "Create", + UpdateAll => "UpdateAll", + Update => "Update", + DeleteAll => "DeleteAll", + Delete => "Delete" + }; + format_ident!("Resource{}", name) + } + + fn fn_ident(&self) -> Ident + { + use Method::*; + + let name = match self { + ReadAll => "read_all", + Read => "read", + Create => "create", + UpdateAll => "update_all", + Update => "update", + DeleteAll => "delete_all", + Delete => "delete" + }; + format_ident!("{}", name) + } +} + +pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream +{ + let ident = parse_macro_input!(attrs as Ident); + let fun = parse_macro_input!(item as ItemFn); + + let ret = match fun.sig.output { + ReturnType::Default => quote!(()), + ReturnType::Type(_, ty) => quote!(#ty) + }; + let args : Vec<(TokenStream2, TokenStream2)> = fun.sig.inputs.iter().map(|arg| match arg { + FnArg::Typed(arg) => { + let pat = &arg.pat; + let ty = &arg.ty; + (quote!(#pat), quote!(#ty)) + }, + FnArg::Receiver(_) => panic!("didn't expect self parameter") + }).collect(); + let mut generics : Vec = Vec::new(); + for i in 1..args.len() + { + let (_, ty) = &args[i]; + generics.push(quote!(#ty)); + } + generics.push(quote!(#ret)); + let args : Vec = args.into_iter().map(|(pat, ty)| quote!(#pat : #ty)).collect(); + let block = fun.block; + + let trait_ident = method.trait_ident(); + let fn_ident = method.fn_ident(); + + let output = quote! { + impl ::gotham_restful::#trait_ident<#(#generics),*> for #ident + where #ident : ::gotham_restful::Resource + { + fn #fn_ident(#(#args),*) -> #ret + #block + } + }; + output.into() +}