mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-07 13:33:17 +00:00
macro: factor out newtype handler
Reuse verifier code of structs fields. Add handling of 'validate' methods for fields. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
dfdf354b0d
commit
818dc25478
@ -429,11 +429,70 @@ fn handle_struct(definition: Object, item: &syn::ItemStruct) -> Result<TokenStre
|
|||||||
|
|
||||||
match item.fields {
|
match item.fields {
|
||||||
syn::Fields::Unit => c_bail!(item.span(), "unit types are not allowed"),
|
syn::Fields::Unit => c_bail!(item.span(), "unit types are not allowed"),
|
||||||
|
syn::Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||||
|
handle_newtype(definition, name, fields)
|
||||||
|
}
|
||||||
syn::Fields::Unnamed(ref fields) => handle_struct_unnamed(definition, name, fields),
|
syn::Fields::Unnamed(ref fields) => handle_struct_unnamed(definition, name, fields),
|
||||||
syn::Fields::Named(ref fields) => handle_struct_named(definition, name, fields),
|
syn::Fields::Named(ref fields) => handle_struct_named(definition, name, fields),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StructField<'i, 't> {
|
||||||
|
def: ParameterDefinition,
|
||||||
|
ident: Option<&'i Ident>,
|
||||||
|
access: syn::Member,
|
||||||
|
mem_id: isize,
|
||||||
|
string: String,
|
||||||
|
strlit: syn::LitStr,
|
||||||
|
ty: &'t syn::Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_newtype(
|
||||||
|
mut definition: Object,
|
||||||
|
name: &Ident,
|
||||||
|
item: &syn::FieldsUnnamed,
|
||||||
|
) -> Result<TokenStream, Error> {
|
||||||
|
let fields = &item.unnamed;
|
||||||
|
let field_punct = fields.first().unwrap();
|
||||||
|
let field = field_punct.value();
|
||||||
|
|
||||||
|
let common = CommonTypeDefinition::from_object(&mut definition)?;
|
||||||
|
let apidef = ParameterDefinition::from_object(definition)?;
|
||||||
|
|
||||||
|
let impl_verify = struct_fields_impl_verify(item.span(), &[StructField {
|
||||||
|
def: apidef,
|
||||||
|
ident: None,
|
||||||
|
access: syn::Member::Unnamed(syn::Index {
|
||||||
|
index: 0,
|
||||||
|
span: name.span(),
|
||||||
|
}),
|
||||||
|
mem_id: 0,
|
||||||
|
string: "0".to_string(),
|
||||||
|
strlit: syn::LitStr::new("0", name.span()),
|
||||||
|
ty: &field.ty,
|
||||||
|
}])?;
|
||||||
|
|
||||||
|
let description = common.description;
|
||||||
|
let parse_cli = common.cli.quote(&name);
|
||||||
|
Ok(quote! {
|
||||||
|
impl ::proxmox::api::ApiType for #name {
|
||||||
|
fn type_info() -> &'static ::proxmox::api::TypeInfo {
|
||||||
|
use ::proxmox::api::cli::ParseCli;
|
||||||
|
use ::proxmox::api::cli::ParseCliFromStr;
|
||||||
|
const INFO: ::proxmox::api::TypeInfo = ::proxmox::api::TypeInfo {
|
||||||
|
name: stringify!(#name),
|
||||||
|
description: #description,
|
||||||
|
complete_fn: None, // FIXME!
|
||||||
|
parse_cli: #parse_cli,
|
||||||
|
};
|
||||||
|
&INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
#impl_verify
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_struct_unnamed(
|
fn handle_struct_unnamed(
|
||||||
mut definition: Object,
|
mut definition: Object,
|
||||||
name: &Ident,
|
name: &Ident,
|
||||||
@ -477,15 +536,6 @@ fn handle_struct_unnamed(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StructField<'i, 't> {
|
|
||||||
def: ParameterDefinition,
|
|
||||||
ident: &'i Ident,
|
|
||||||
mem_id: isize,
|
|
||||||
string: String,
|
|
||||||
strlit: syn::LitStr,
|
|
||||||
ty: &'t syn::Type,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_struct_named(
|
fn handle_struct_named(
|
||||||
mut definition: Object,
|
mut definition: Object,
|
||||||
type_ident: &Ident,
|
type_ident: &Ident,
|
||||||
@ -538,7 +588,8 @@ fn handle_struct_named(
|
|||||||
let def = ParameterDefinition::from_expression(def)?;
|
let def = ParameterDefinition::from_expression(def)?;
|
||||||
fields.push(StructField {
|
fields.push(StructField {
|
||||||
def,
|
def,
|
||||||
ident: field_ident,
|
ident: Some(field_ident),
|
||||||
|
access: syn::Member::Named(field_ident.clone()),
|
||||||
mem_id,
|
mem_id,
|
||||||
string: field_string,
|
string: field_string,
|
||||||
strlit: field_strlit,
|
strlit: field_strlit,
|
||||||
@ -546,7 +597,7 @@ fn handle_struct_named(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let impl_verify = named_struct_impl_verify(item.span(), &fields)?;
|
let impl_verify = struct_fields_impl_verify(item.span(), &fields)?;
|
||||||
let (impl_serialize, impl_deserialize) = if serialize_as_string {
|
let (impl_serialize, impl_deserialize) = if serialize_as_string {
|
||||||
let expected = format!("valid {}", type_ident);
|
let expected = format!("valid {}", type_ident);
|
||||||
(
|
(
|
||||||
@ -599,15 +650,15 @@ fn handle_struct_named(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenStream, Error> {
|
fn struct_fields_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenStream, Error> {
|
||||||
let mut body = TokenStream::new();
|
let mut body = TokenStream::new();
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let field_ident = field.ident;
|
let field_access = &field.access;
|
||||||
let field_str = &field.strlit;
|
let field_str = &field.strlit;
|
||||||
|
|
||||||
// first of all, recurse into the contained types:
|
// first of all, recurse into the contained types:
|
||||||
body.extend(quote_spanned! { field_ident.span() =>
|
body.extend(quote_spanned! { field_access.span() =>
|
||||||
::proxmox::api::ApiType::verify(&self.#field_ident)?;
|
::proxmox::api::ApiType::verify(&self.#field_access)?;
|
||||||
});
|
});
|
||||||
|
|
||||||
// then go through all the additional verifiers:
|
// then go through all the additional verifiers:
|
||||||
@ -615,7 +666,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
if let Some(ref value) = field.def.minimum {
|
if let Some(ref value) = field.def.minimum {
|
||||||
body.extend(quote_spanned! { value.span() =>
|
body.extend(quote_spanned! { value.span() =>
|
||||||
let value = #value;
|
let value = #value;
|
||||||
if !::proxmox::api::verify::TestMinMax::test_minimum(&self.#field_ident, &value) {
|
if !::proxmox::api::verify::TestMinMax::test_minimum(&self.#field_access, &value) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
&format!("field {} out of range, must be >= {}", #field_str, value)
|
&format!("field {} out of range, must be >= {}", #field_str, value)
|
||||||
);
|
);
|
||||||
@ -626,7 +677,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
if let Some(ref value) = field.def.maximum {
|
if let Some(ref value) = field.def.maximum {
|
||||||
body.extend(quote_spanned! { value.span() =>
|
body.extend(quote_spanned! { value.span() =>
|
||||||
let value = #value;
|
let value = #value;
|
||||||
if !::proxmox::api::verify::TestMinMax::test_maximum(&self.#field_ident, &value) {
|
if !::proxmox::api::verify::TestMinMax::test_maximum(&self.#field_access, &value) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
&format!("field {} out of range, must be <= {}", #field_str, value)
|
&format!("field {} out of range, must be <= {}", #field_str, value)
|
||||||
);
|
);
|
||||||
@ -638,7 +689,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
body.extend(quote_spanned! { value.span() =>
|
body.extend(quote_spanned! { value.span() =>
|
||||||
let value = #value;
|
let value = #value;
|
||||||
if !::proxmox::api::verify::TestMinMaxLen::test_minimum_length(
|
if !::proxmox::api::verify::TestMinMaxLen::test_minimum_length(
|
||||||
&self.#field_ident,
|
&self.#field_access,
|
||||||
value,
|
value,
|
||||||
) {
|
) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
@ -652,7 +703,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
body.extend(quote_spanned! { value.span() =>
|
body.extend(quote_spanned! { value.span() =>
|
||||||
let value = #value;
|
let value = #value;
|
||||||
if !::proxmox::api::verify::TestMinMaxLen::test_maximum_length(
|
if !::proxmox::api::verify::TestMinMaxLen::test_maximum_length(
|
||||||
&self.#field_ident,
|
&self.#field_access,
|
||||||
value,
|
value,
|
||||||
) {
|
) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
@ -664,7 +715,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
|
|
||||||
if let Some(ref value) = field.def.format {
|
if let Some(ref value) = field.def.format {
|
||||||
body.extend(quote_spanned! { value.span() =>
|
body.extend(quote_spanned! { value.span() =>
|
||||||
if !#value::verify(&self.#field_ident) {
|
if !#value::verify(&self.#field_access) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
&format!("field {} does not match format {}", #field_str, #value::NAME)
|
&format!("field {} does not match format {}", #field_str, #value::NAME)
|
||||||
);
|
);
|
||||||
@ -679,7 +730,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
::lazy_static::lazy_static! {
|
::lazy_static::lazy_static! {
|
||||||
static ref RE: ::regex::Regex = ::regex::Regex::new(#regex).unwrap();
|
static ref RE: ::regex::Regex = ::regex::Regex::new(#regex).unwrap();
|
||||||
}
|
}
|
||||||
if !RE.is_match(&self.#field_ident) {
|
if !RE.is_match(&self.#field_access) {
|
||||||
error_string.push_str(&format!(
|
error_string.push_str(&format!(
|
||||||
"field {} does not match the allowed pattern: {}",
|
"field {} does not match the allowed pattern: {}",
|
||||||
#field_str,
|
#field_str,
|
||||||
@ -689,7 +740,7 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
regex => body.extend(quote_spanned! { value.span() =>
|
regex => body.extend(quote_spanned! { value.span() =>
|
||||||
if !#regex.is_match(&self.#field_ident) {
|
if !#regex.is_match(&self.#field_access) {
|
||||||
error_string.push_str(
|
error_string.push_str(
|
||||||
&format!("field {} does not match the allowed pattern", #field_str)
|
&format!("field {} does not match the allowed pattern", #field_str)
|
||||||
);
|
);
|
||||||
@ -697,6 +748,14 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref value) = field.def.validate {
|
||||||
|
body.extend(quote_spanned! { value.span() =>
|
||||||
|
if let Err(err) = #value(&self.#field_access) {
|
||||||
|
error_string.push_str(&err.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !body.is_empty() {
|
if !body.is_empty() {
|
||||||
@ -760,8 +819,8 @@ fn named_struct_derive_serialize(
|
|||||||
|
|
||||||
let mut entries = TokenStream::new();
|
let mut entries = TokenStream::new();
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let field_ident = field.ident;
|
let field_ident = field.ident.unwrap();
|
||||||
let field_span = field.ident.span();
|
let field_span = field_ident.span();
|
||||||
let field_str = &field.strlit;
|
let field_str = &field.strlit;
|
||||||
match field.def.serialize_with.as_ref() {
|
match field.def.serialize_with.as_ref() {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
@ -857,8 +916,8 @@ fn named_struct_derive_deserialize(
|
|||||||
let mut field_option_init_list = TokenStream::new();
|
let mut field_option_init_list = TokenStream::new();
|
||||||
let mut field_value_matches = TokenStream::new();
|
let mut field_value_matches = TokenStream::new();
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let field_ident = field.ident;
|
let field_ident = field.ident.unwrap();
|
||||||
let field_span = field.ident.span();
|
let field_span = field_ident.span();
|
||||||
let field_str = &field.strlit;
|
let field_str = &field.strlit;
|
||||||
let mem_id = field.mem_id;
|
let mem_id = field.mem_id;
|
||||||
|
|
||||||
@ -1047,11 +1106,11 @@ fn named_struct_impl_default(
|
|||||||
for field in fields {
|
for field in fields {
|
||||||
let field_ident = field.ident;
|
let field_ident = field.ident;
|
||||||
if let Some(ref default) = field.def.default {
|
if let Some(ref default) = field.def.default {
|
||||||
entries.extend(quote_spanned! { field.ident.span() =>
|
entries.extend(quote_spanned! { field_ident.span() =>
|
||||||
#field_ident: #default.into(),
|
#field_ident: #default.into(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
entries.extend(quote_spanned! { field.ident.span() =>
|
entries.extend(quote_spanned! { field_ident.span() =>
|
||||||
#field_ident: Default::default(),
|
#field_ident: Default::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user