Add #[schemars(inner(...)] attribute to specify schema for array items (#234)
This commit is contained in:
parent
30e513ac14
commit
a5e51b22b3
11 changed files with 231 additions and 60 deletions
|
@ -43,13 +43,17 @@ pub struct ValidationAttrs {
|
|||
contains: Option<String>,
|
||||
required: bool,
|
||||
format: Option<Format>,
|
||||
inner: Option<Box<ValidationAttrs>>,
|
||||
}
|
||||
|
||||
impl ValidationAttrs {
|
||||
pub fn new(attrs: &[syn::Attribute], errors: &Ctxt) -> Self {
|
||||
let schemars_items = get_meta_items(attrs, "schemars", errors, false);
|
||||
let validate_items = get_meta_items(attrs, "validate", errors, true);
|
||||
|
||||
ValidationAttrs::default()
|
||||
.populate(attrs, "schemars", false, errors)
|
||||
.populate(attrs, "validate", true, errors)
|
||||
.populate(schemars_items, "schemars", false, errors)
|
||||
.populate(validate_items, "validate", true, errors)
|
||||
}
|
||||
|
||||
pub fn required(&self) -> bool {
|
||||
|
@ -58,7 +62,7 @@ impl ValidationAttrs {
|
|||
|
||||
fn populate(
|
||||
mut self,
|
||||
attrs: &[syn::Attribute],
|
||||
meta_items: Vec<syn::NestedMeta>,
|
||||
attr_type: &'static str,
|
||||
ignore_errors: bool,
|
||||
errors: &Ctxt,
|
||||
|
@ -97,11 +101,7 @@ impl ValidationAttrs {
|
|||
}
|
||||
};
|
||||
|
||||
for meta_item in attrs
|
||||
.iter()
|
||||
.flat_map(|attr| get_meta_items(attr, attr_type, errors, ignore_errors))
|
||||
.flatten()
|
||||
{
|
||||
for meta_item in meta_items {
|
||||
match &meta_item {
|
||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("length") => {
|
||||
for nested in meta_list.nested.iter() {
|
||||
|
@ -247,7 +247,8 @@ impl ValidationAttrs {
|
|||
if !ignore_errors {
|
||||
errors.error_spanned_by(
|
||||
meta,
|
||||
"unknown item in schemars regex attribute".to_string(),
|
||||
"unknown item in schemars regex attribute"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +293,8 @@ impl ValidationAttrs {
|
|||
if !ignore_errors {
|
||||
errors.error_spanned_by(
|
||||
meta,
|
||||
"unknown item in schemars contains attribute".to_string(),
|
||||
"unknown item in schemars contains attribute"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +304,21 @@ impl ValidationAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("inner") => {
|
||||
match self.inner {
|
||||
Some(_) => duplicate_error(&meta_list.path),
|
||||
None => {
|
||||
let inner_attrs = ValidationAttrs::default().populate(
|
||||
meta_list.nested.clone().into_iter().collect(),
|
||||
attr_type,
|
||||
ignore_errors,
|
||||
errors,
|
||||
);
|
||||
self.inner = Some(Box::new(inner_attrs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -309,16 +326,24 @@ impl ValidationAttrs {
|
|||
}
|
||||
|
||||
pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
|
||||
if let Some(apply_expr) = self.apply_to_schema_expr() {
|
||||
*schema_expr = quote! {
|
||||
{
|
||||
let mut schema = #schema_expr;
|
||||
#apply_expr
|
||||
schema
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_to_schema_expr(&self) -> Option<TokenStream> {
|
||||
let mut array_validation = Vec::new();
|
||||
let mut number_validation = Vec::new();
|
||||
let mut object_validation = Vec::new();
|
||||
let mut string_validation = Vec::new();
|
||||
|
||||
if let Some(length_min) = self
|
||||
.length_min
|
||||
.as_ref()
|
||||
.or(self.length_equal.as_ref())
|
||||
{
|
||||
if let Some(length_min) = self.length_min.as_ref().or(self.length_equal.as_ref()) {
|
||||
string_validation.push(quote! {
|
||||
validation.min_length = Some(#length_min as u32);
|
||||
});
|
||||
|
@ -327,11 +352,7 @@ impl ValidationAttrs {
|
|||
});
|
||||
}
|
||||
|
||||
if let Some(length_max) = self
|
||||
.length_max
|
||||
.as_ref()
|
||||
.or(self.length_equal.as_ref())
|
||||
{
|
||||
if let Some(length_max) = self.length_max.as_ref().or(self.length_equal.as_ref()) {
|
||||
string_validation.push(quote! {
|
||||
validation.max_length = Some(#length_max as u32);
|
||||
});
|
||||
|
@ -378,6 +399,21 @@ impl ValidationAttrs {
|
|||
}
|
||||
});
|
||||
|
||||
let inner_validation = self
|
||||
.inner
|
||||
.as_deref()
|
||||
.and_then(|inner| inner.apply_to_schema_expr())
|
||||
.map(|apply_expr| {
|
||||
quote! {
|
||||
if schema_object.has_type(schemars::schema::InstanceType::Array) {
|
||||
if let Some(schemars::schema::SingleOrVec::Single(inner_schema)) = &mut schema_object.array().items {
|
||||
let mut schema = &mut **inner_schema;
|
||||
#apply_expr
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let array_validation = wrap_array_validation(array_validation);
|
||||
let number_validation = wrap_number_validation(number_validation);
|
||||
let object_validation = wrap_object_validation(object_validation);
|
||||
|
@ -388,21 +424,20 @@ impl ValidationAttrs {
|
|||
|| object_validation.is_some()
|
||||
|| string_validation.is_some()
|
||||
|| format.is_some()
|
||||
|| inner_validation.is_some()
|
||||
{
|
||||
*schema_expr = quote! {
|
||||
{
|
||||
let mut schema = #schema_expr;
|
||||
if let schemars::schema::Schema::Object(schema_object) = &mut schema
|
||||
{
|
||||
#array_validation
|
||||
#number_validation
|
||||
#object_validation
|
||||
#string_validation
|
||||
#format
|
||||
}
|
||||
schema
|
||||
Some(quote! {
|
||||
if let schemars::schema::Schema::Object(schema_object) = &mut schema {
|
||||
#array_validation
|
||||
#number_validation
|
||||
#object_validation
|
||||
#string_validation
|
||||
#format
|
||||
#inner_validation
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue