#![recursion_limit = "256"] extern crate proc_macro; extern crate proc_macro2; use proc_macro::TokenStream; #[macro_use] mod util; mod api_def; mod parsing; mod types; mod api_macro; mod router_macro; fn handle_error( mut item: proc_macro2::TokenStream, kind: &'static str, err: failure::Error, ) -> TokenStream { match err.downcast::() { Ok(err) => { let err: proc_macro2::TokenStream = err.to_compile_error().into(); item.extend(err); item.into() } Err(err) => panic!("error in {}: {}", kind, err), } } /// This is the `#[api(api definition)]` attribute for functions. An Api definition defines the /// parameters and return type of an API call. The function will automatically be wrapped in a /// function taking and returning a json `Value`, while performing validity checks on both input /// and output. /// /// Example: /// ```ignore /// #[api({ /// parameters: { /// // Short form: [`optional`] TYPE ("description") /// name: string ("A person's name"), /// gender: optional string ("A person's gender"), /// // Long form uses json-ish syntax: /// coolness: { /// type: integer, // we don't enclose type names in quotes though... /// description: "the coolness of a person, using the coolness scale", /// minimum: 0, /// maximum: 10, /// }, /// // Hyphenated parameters are allowed, but need quotes (due to how proc_macro /// // TokenStreams work) /// "is-weird": optional float ("hyphenated names must be enclosed in quotes") /// }, /// // TODO: returns: {} /// })] /// fn test() { /// } /// ``` #[proc_macro_attribute] pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { let item: proc_macro2::TokenStream = item.into(); match api_macro::api_macro(attr.into(), item.clone()) { Ok(output) => output.into(), Err(err) => handle_error(item, "api definition", err), } } /// The router macro helps to avoid having to type out strangely nested `Router` expressions. /// /// Note that without `proc_macro_hack` we currently cannot use macros in expression position, so /// this cannot be used inline within an expression. /// /// Example: /// ```ignore /// router!{ /// let my_router = { /// /people/{person}: { /// POST: create_person, /// GET: get_person, /// PUT: update_person, /// DELETE: delete_person, /// }, /// /people/{person}/kick: { POST: kick_person }, /// /groups/{group}: { /// /: { /// POST: create_group, /// PUT: update_group_info, /// GET: get_group_info, /// DELETE: delete_group, /// }, /// /people/{person}: { /// POST: add_person_to_group, /// DELETE: delete_person_from_group, /// PUT: update_person_details_for_group, /// GET: get_person_details_from_group, /// }, /// }, /// /other: (an_external_router) /// }; /// } /// ``` /// /// The above should produce the following output: /// ```ignore /// let my_router = Router::new() /// .subdir( /// "people", /// Router::new() /// .parameter_subdir( /// "person", /// Router::new() /// .post(create_person) /// .get(get_person) /// .put(update_person) /// .delete(delete_person) /// .subdir( /// "kick", /// Router::new() /// .post(kick_person) /// ) /// ) /// ) /// .subdir( /// "groups", /// Router::new() /// .parameter_subdir( /// "group", /// Router::new() /// .post(create_group) /// .put(update_group_info) /// .get(get_group_info) /// .delete(delete_group_info) /// .subdir( /// "people", /// Router::new() /// .parameter_subdir( /// "person", /// Router::new() /// .post(add_person_to_group) /// .delete(delete_person_from_group) /// .put(update_person_details_for_group) /// .get(get_person_details_from_group) /// ) /// ) /// ) /// ) /// .subdir("other", an_external_router) /// ; /// ``` #[proc_macro] pub fn router(input: TokenStream) -> TokenStream { // TODO... let input: proc_macro2::TokenStream = input.into(); match router_macro::router_macro(input.clone()) { Ok(output) => output.into(), Err(err) => handle_error(input, "router", err), } }