domcom_form_manager/
lib.rs1use std::collections::HashMap;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use regex::Regex;
6
7const FIELD_PAT: &str = r#"\.field\(((.|\n)+?)\)"#;
21const FIELD_ARGS_PAT: &str = r#"([A-z]+? *: *(?:".+?"|.+? *(?:,|$)))"#;
22
23#[proc_macro]
24pub fn form(item: TokenStream) -> TokenStream {
25 let string = item.to_string();
26 let fields = extract_fields(string);
27 TokenStream::from(quote! {
28 vec![
29 #( #fields )*
30 ]
31 })
32}
33
34fn extract_fields(string: String) -> Vec<proc_macro2::TokenStream> {
35 let fields = Regex::new(FIELD_PAT).unwrap();
36 let fields = fields.captures_iter(&string);
37 let mut quotes = Vec::<proc_macro2::TokenStream>::new();
38
39 for field in fields.into_iter() {
40 let field = field[1].trim().replace("\n", "");
41
42 let args = Regex::new(FIELD_ARGS_PAT).unwrap();
43 let args = args.captures_iter(&field);
44
45 let mut map = HashMap::<String, String>::new();
46 for arg in args.into_iter() {
47 let arg = arg[0].trim();
48 let arg = arg.split(":").collect::<Vec<&str>>();
49
50 let key = arg[0].trim();
51 let mut value = arg[1].trim();
52
53 if value.ends_with(',') {
54 value = value[..value.len() - 1].trim();
55 }
56
57 if value.starts_with('"') && value.ends_with('"') {
58 value = &value[1..value.len() - 1];
59 }
60
61 map.insert(key.to_string(), value.to_string());
62 }
63
64 let d = String::new();
65
66 let label = map.get("label").unwrap();
67 let required = map
68 .get("required")
69 .map(|x| x.parse::<bool>().unwrap())
70 .unwrap_or(false);
71
72 let pattern = map.get("pattern");
73 let pattern = pattern.unwrap_or(&d);
74
75 let field_type = map.get("type").unwrap().as_str();
76 let field_type = format_ident!("{}", field_type);
77
78 quotes.push(quote! {
79 Field::<#field_type> {
80 label: #label,
81 value: futures_signals::signal::Mutable::new(),
82 error: futures_signals::signal::Mutable::new(),
83 regex: #pattern,
84 required: #required,
85 },
86 });
87 }
88
89 quotes
90}