domcom_form_manager/
lib.rs

1use std::collections::HashMap;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use regex::Regex;
6
7/*
8
9form! {
10    .field(
11        id: "first_name"
12        label: "First Name",
13        pattern: ".{3,}",
14        required: true
15    )
16}
17
18*/
19
20const 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}