1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
//! # New Home Application (Macro) //! //! This crate provides a macro for deriving the [Method](new_home_application::method::Method) trait. //! //! ## Attributes //! //! There are a couple of attributes that can be used on the struct and/or fields in the struct //! //! | Name | Scope | Description | //! |---------------|--------|------------------------------------------------------------------------| //! | `description` | Struct | Provides a description for the method | //! | `help` | Struct | Provides a help text for an argument/struct field or the method itself | //! //! ## Arguments //! //! The word "Arguments" describes fields on the Method struct, that are set through the [Method::set_arguments](new_home_application::method::Method::set_arguments) //! method. While you can implement the Method trait by yourself and just handle it on your own, this //! macro allows you to have the `argument` attribute on a field which causes the field to be automatically //! set by the set_arguments method. This however requires the field to have the type [serde_json::Value]. //! //! The argument attribute also has to have a content, which will become the description for the field. //! //! To set a default value for the argument, there is the `default` attribute. The content of this //! attribute will be literally passed to the [serde_json::Value]::from method. So you will have to //! wrap strings in quotes. On the other side however, you can easily put a plain number or boolean //! in there and it will be filled in to the argument. //! //! ## Example //! //! You can see a full example in the `examples/macro-example.rs` file, but here you also have a small //! example for applying the macro onto a struct: //! //! ```rust //! use new_home_application::method::{Method, MethodCallable}; //! use new_home_application::communication::{MethodCall, MethodResult}; //! use new_home_application_macro::Method; //! //! use serde::Deserialize; //! //! /// The arguments used in the callable later //! #[derive(Deserialize)] //! struct Arguments { //! /// The word that should be converted //! pub word: String, //! //! /// Determines if the word will be converted to uppercase //! pub uppercase: bool, //! } //! //! #[derive(Method)] //! #[description("Can convert a word into uppercase")] //! #[help("Will convert the word to uppercase if uppercase flag is set")] //! struct ExampleMethod; //! //! impl MethodCallable for ExampleMethod { //! type ArgumentsType = Arguments; //! //! fn secure_call(&mut self, name: String, arguments: Self::ArgumentsType) -> MethodResult { //! unimplemented!() //! } //! } //! ``` //! //! The logic for executing whatever the method should do is not implemented here as the Method trait //! only provides basic information for the method. The logic will be added through the [MethodCallable](new_home_application::method::MethodCallable) //! trait which cannot be derived and has to be implemented by yourself. //! extern crate inflector; extern crate new_home_application; extern crate quote; extern crate serde; extern crate syn; extern crate toml; use proc_macro::TokenStream; use syn::export::ToTokens; use syn::{DeriveInput}; fn build_impl(struct_name: String, description: String, help: String) -> String { format!( r##" impl new_home_application::method::Method for {struct_name} {{ fn name(&self) -> String {{ String::from(r#"{name}"#) }} fn description(&self) -> String {{ String::from(r#"{description}"#) }} fn help(&self) -> String {{ String::from(r#"{help}"#) }} }} "##, struct_name = struct_name.clone(), name = inflector::cases::snakecase::to_snake_case(struct_name.as_str()), description = description, help = help, ) } #[proc_macro_derive(Method, attributes(default, description, argument, help))] pub fn derive_method(input: TokenStream) -> TokenStream { let ast = syn::parse::<DeriveInput>(input.clone()).unwrap(); let mut description = String::new(); let mut help = String::new(); for attr in ast.attrs { let name = attr.path.to_token_stream().to_string(); let value = String::from( attr.tokens .to_string() .trim_start() .trim_end() .trim_start_matches("(") .trim_end_matches(")") .trim_start_matches("\"") .trim_end_matches("\""), ); if name.eq("description") { description = value; continue; } if name.eq("help") { help = value; continue; } } build_impl(ast.ident.to_string(), description, help) .parse() .unwrap() }