use openapiv3::{
ArrayType, IntegerType, NumberType, ReferenceOr, Schema, SchemaKind, StringType, Type,
};
use proc_macro2::{Literal, TokenStream};
use quote::quote;
#[must_use]
pub fn validation_attrs(prop: &ReferenceOr<Schema>) -> TokenStream {
match prop {
ReferenceOr::Reference { .. } => quote! { #[validate] },
ReferenceOr::Item(schema) => schema_attrs(schema),
}
}
fn schema_attrs(schema: &Schema) -> TokenStream {
match &schema.schema_kind {
SchemaKind::Type(Type::String(s)) => string_attrs(s),
SchemaKind::Type(Type::Integer(i)) => integer_attrs(i),
SchemaKind::Type(Type::Number(n)) => number_attrs(n),
SchemaKind::Type(Type::Array(a)) => array_attrs(a),
SchemaKind::Type(Type::Object(obj)) if !obj.properties.is_empty() => quote! { #[validate] },
SchemaKind::OneOf { .. } | SchemaKind::AnyOf { .. } | SchemaKind::AllOf { .. } => {
quote! { #[validate] }
}
_ => quote! {},
}
}
fn string_attrs(s: &StringType) -> TokenStream {
let mut attrs = Vec::new();
if let Some(min) = s.min_length {
let lit = Literal::usize_unsuffixed(min);
attrs.push(quote! { #[validate(min_length = #lit)] });
}
if let Some(max) = s.max_length {
let lit = Literal::usize_unsuffixed(max);
attrs.push(quote! { #[validate(max_length = #lit)] });
}
if let Some(pattern) = &s.pattern {
attrs.push(quote! { #[validate(pattern = #pattern)] });
}
quote! { #(#attrs)* }
}
fn integer_attrs(i: &IntegerType) -> TokenStream {
let min = i.minimum.map(Literal::i64_unsuffixed);
let max = i.maximum.map(Literal::i64_unsuffixed);
let mult = i.multiple_of.map(Literal::i64_unsuffixed);
bound_attrs(min, i.exclusive_minimum, max, i.exclusive_maximum, mult)
}
fn number_attrs(n: &NumberType) -> TokenStream {
let min = n.minimum.map(Literal::f64_unsuffixed);
let max = n.maximum.map(Literal::f64_unsuffixed);
let mult = n.multiple_of.map(Literal::f64_unsuffixed);
bound_attrs(min, n.exclusive_minimum, max, n.exclusive_maximum, mult)
}
fn bound_attrs(
min: Option<Literal>,
exclusive_min: bool,
max: Option<Literal>,
exclusive_max: bool,
multiple_of: Option<Literal>,
) -> TokenStream {
let mut attrs = Vec::new();
if let Some(min) = min {
if exclusive_min {
attrs.push(quote! { #[validate(exclusive_minimum = #min)] });
} else {
attrs.push(quote! { #[validate(minimum = #min)] });
}
}
if let Some(max) = max {
if exclusive_max {
attrs.push(quote! { #[validate(exclusive_maximum = #max)] });
} else {
attrs.push(quote! { #[validate(maximum = #max)] });
}
}
if let Some(mult) = multiple_of {
attrs.push(quote! { #[validate(multiple_of = #mult)] });
}
quote! { #(#attrs)* }
}
fn array_attrs(a: &ArrayType) -> TokenStream {
let mut attrs = Vec::new();
if let Some(min) = a.min_items {
let lit = Literal::usize_unsuffixed(min);
attrs.push(quote! { #[validate(min_items = #lit)] });
}
if let Some(max) = a.max_items {
let lit = Literal::usize_unsuffixed(max);
attrs.push(quote! { #[validate(max_items = #lit)] });
}
if a.unique_items {
attrs.push(quote! { #[validate(unique_items)] });
}
quote! { #(#attrs)* }
}