]> git.proxmox.com Git - proxmox.git/commitdiff
macro: support 'pattern' verifier in structs
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Wed, 31 Jul 2019 12:32:35 +0000 (14:32 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Wed, 31 Jul 2019 12:32:35 +0000 (14:32 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
proxmox-api-macro/src/api_def.rs
proxmox-api-macro/src/api_macro.rs

index f756d45d352b231588b6d6a1f0503a2e5611737b..b7afe8e2bb1bc12b6d254bbddb119699280d699a 100644 (file)
@@ -108,6 +108,12 @@ pub struct ParameterDefinition {
     #[builder(default)]
     pub format: Option<syn::Path>,
 
+    /// Patterns are regular expressions. When a literal string is provided, a `lazy_static` regex
+    /// is created for the verifier. Otherwise it is taken as an expression (i.e. a path) to an
+    /// existing regex variable/method.
+    #[builder(default)]
+    pub pattern: Option<syn::Expr>,
+
     #[builder(default)]
     pub serialize_with: Option<syn::Path>,
     #[builder(default)]
@@ -149,6 +155,9 @@ impl ParameterDefinition {
                 "format" => {
                     def.format(Some(value.expect_path()?));
                 }
+                "pattern" => {
+                    def.pattern(Some(value.expect_expr()?));
+                }
                 "serialize_with" => {
                     def.serialize_with(Some(value.expect_path()?));
                 }
index 108f72ab1e55b87fddae3a2e1a3ddb59906849e4..71193502929d50fa1d3ee84d18fadabd9991b3d3 100644 (file)
@@ -510,6 +510,12 @@ fn handle_struct_named(
         bail!("derive_default is not finished");
     }
 
+    let serialize_as_string = definition
+        .remove("serialize_as_string")
+        .map(|e| e.expect_lit_bool_direct())
+        .transpose()?
+        .unwrap_or(false);
+
     let type_s = type_ident.to_string();
     let type_span = type_ident.span();
     let type_str = syn::LitStr::new(&type_s, type_span);
@@ -541,10 +547,23 @@ fn handle_struct_named(
     }
 
     let impl_verify = named_struct_impl_verify(item.span(), &fields)?;
-    let impl_serialize =
-        named_struct_derive_serialize(item.span(), type_ident, &type_str, &fields)?;
-    let impl_deserialize =
-        named_struct_derive_deserialize(item.span(), type_ident, &type_str, &fields)?;
+    let (impl_serialize, impl_deserialize) = if serialize_as_string {
+        let expected = format!("valid {}", type_ident);
+        (
+            quote_spanned! { item.span() =>
+                ::serde_plain::derive_serialize_from_display!(#type_ident);
+            },
+            quote_spanned! { item.span() =>
+                ::serde_plain::derive_deserialize_from_str!(#type_ident, #expected);
+            },
+        )
+    } else {
+        (
+            named_struct_derive_serialize(item.span(), type_ident, &type_str, &fields)?,
+            named_struct_derive_deserialize(item.span(), type_ident, &type_str, &fields)?,
+        )
+    };
+
     let accessors = named_struct_impl_accessors(item.span(), type_ident, &fields)?;
 
     let impl_default = if derive_default {
@@ -645,6 +664,32 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
                 }
             });
         }
+
+        if let Some(ref value) = field.def.pattern {
+            match value {
+                syn::Expr::Lit(regex) => body.extend(quote_spanned! { value.span() =>
+                    {
+                        ::lazy_static::lazy_static! {
+                            static ref RE: ::regex::Regex = ::regex::Regex::new(#regex).unwrap();
+                        }
+                        if !RE.is_match(&self.#field_ident) {
+                            error_string.push_str(&format!(
+                                "field {} does not match the allowed pattern: {}",
+                                #field_str,
+                                #regex,
+                            ));
+                        }
+                    }
+                }),
+                regex => body.extend(quote_spanned! { value.span() =>
+                    if !#regex.is_match(&self.#field_ident) {
+                        error_string.push_str(
+                            &format!("field {} does not match the allowed pattern", #field_str)
+                        );
+                    }
+                }),
+            }
+        }
     }
 
     if !body.is_empty() {