diff --git a/Cargo.toml b/Cargo.toml index 6998a0979..9bd6ea23d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "0.71.1" +version = "0.72.0" authors = ["Chrislearn Young "] edition = "2021" rust-version = "1.80" @@ -19,24 +19,24 @@ license = "MIT OR Apache-2.0" categories = ["web-programming::http-server", "web-programming::websocket", "network-programming", "asynchronous"] [workspace.dependencies] -salvo_macros = { version = "0.71.1", path = "crates/macros", default-features = false } -salvo_core = { version = "0.71.1", path = "crates/core", default-features = false } -salvo_extra = { version = "0.71.1", path = "crates/extra", default-features = false } -salvo-compression = { version = "0.71.1", path = "crates/compression", default-features = false } -salvo-cache = { version = "0.71.1", path = "crates/cache", default-features = false } -salvo-cors = { version = "0.71.1", path = "crates/cors", default-features = false } -salvo-csrf = { version = "0.71.1", path = "crates/csrf", default-features = false } -salvo-flash = { version = "0.71.1", path = "crates/flash", default-features = false } +salvo_macros = { version = "0.72.0", path = "crates/macros", default-features = false } +salvo_core = { version = "0.72.0", path = "crates/core", default-features = false } +salvo_extra = { version = "0.72.0", path = "crates/extra", default-features = false } +salvo-compression = { version = "0.72.0", path = "crates/compression", default-features = false } +salvo-cache = { version = "0.72.0", path = "crates/cache", default-features = false } +salvo-cors = { version = "0.72.0", path = "crates/cors", default-features = false } +salvo-csrf = { version = "0.72.0", path = "crates/csrf", default-features = false } +salvo-flash = { version = "0.72.0", path = "crates/flash", default-features = false } salvo-http3 = { version = "0.3.0", default-features = false } -salvo-jwt-auth = { version = "0.71.1", path = "crates/jwt-auth", default-features = false } -salvo-oapi = { version = "0.71.1", path = "./crates/oapi", default-features = false } -salvo-oapi-macros = { version = "0.71.1", path = "crates/oapi-macros", default-features = false } -salvo-otel = { version = "0.71.1", path = "crates/otel", default-features = false } -salvo-proxy = { version = "0.71.1", path = "crates/proxy", default-features = false } -salvo-rate-limiter = { version = "0.71.1", path = "crates/rate-limiter", default-features = false } -salvo-serde-util = { version = "0.71.1", path = "crates/serde-util", default-features = true } -salvo-serve-static = { version = "0.71.1", path = "crates/serve-static", default-features = false } -salvo-session = { version = "0.71.1", path = "crates/session", default-features = false } +salvo-jwt-auth = { version = "0.72.0", path = "crates/jwt-auth", default-features = false } +salvo-oapi = { version = "0.72.0", path = "./crates/oapi", default-features = false } +salvo-oapi-macros = { version = "0.72.0", path = "crates/oapi-macros", default-features = false } +salvo-otel = { version = "0.72.0", path = "crates/otel", default-features = false } +salvo-proxy = { version = "0.72.0", path = "crates/proxy", default-features = false } +salvo-rate-limiter = { version = "0.72.0", path = "crates/rate-limiter", default-features = false } +salvo-serde-util = { version = "0.72.0", path = "crates/serde-util", default-features = true } +salvo-serve-static = { version = "0.72.0", path = "crates/serve-static", default-features = false } +salvo-session = { version = "0.72.0", path = "crates/session", default-features = false } aead = "0.5" aes-gcm = "0.10" diff --git a/crates/oapi-macros/src/component.rs b/crates/oapi-macros/src/component.rs index e66a1209e..746999271 100644 --- a/crates/oapi-macros/src/component.rs +++ b/crates/oapi-macros/src/component.rs @@ -3,10 +3,9 @@ use quote::{quote, quote_spanned, ToTokens}; use syn::spanned::Spanned; use crate::doc_comment::CommentAttributes; -use crate::feature::{ - pop_feature, AdditionalProperties, Description, Feature, FeaturesExt, IsInline, Minimum, - Nullable, TryToTokensExt, Validatable, -}; +use crate::feature::attributes::{AdditionalProperties, Description, Nullable}; +use crate::feature::validation::Minimum; +use crate::feature::{pop_feature, Feature, FeaturesExt, IsInline, TryToTokensExt, Validatable}; use crate::schema_type::{SchemaFormat, SchemaType, SchemaTypeInner}; use crate::type_tree::{GenericType, TypeTree, ValueType}; use crate::{Deprecated, DiagResult, Diagnostic, IntoInner, TryToTokens}; diff --git a/crates/oapi-macros/src/feature/items.rs b/crates/oapi-macros/src/feature/attributes.rs similarity index 66% rename from crates/oapi-macros/src/feature/items.rs rename to crates/oapi-macros/src/feature/attributes.rs index 42507315d..74f3e5ee5 100644 --- a/crates/oapi-macros/src/feature/items.rs +++ b/crates/oapi-macros/src/feature/attributes.rs @@ -1,13 +1,13 @@ use std::{fmt::Display, mem}; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use syn::parse::ParseStream; use syn::punctuated::Punctuated; use syn::token::Comma; use syn::{parenthesized, token, LitStr, Token, Type, TypePath, WherePredicate}; -use super::{impl_get_name, parse_integer, parse_number, Feature, Parse, Validate, Validator}; +use super::{impl_get_name, Feature, Parse}; use crate::{ parameter::{self, ParameterStyle}, parse_utils, schema, @@ -533,408 +533,6 @@ impl From for Feature { } impl_get_name!(ToParametersNames = "names"); -#[derive(Clone, Debug)] -pub(crate) struct MultipleOf(pub(crate) f64, pub(crate) Ident); -impl Validate for MultipleOf { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err(Diagnostic::spanned( - self.1.span(), - DiagLevel::Error, - format!("`multiple_of` error: {}", error), - ) - .help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof`", - )) - } else { - Ok(()) - } - } -} -impl Parse for MultipleOf { - fn parse(input: ParseStream, ident: Ident) -> syn::Result { - parse_number(input).map(|multiple_of| Self(multiple_of, ident)) - } -} -impl ToTokens for MultipleOf { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MultipleOf) -> Self { - Feature::MultipleOf(value) - } -} -impl_get_name!(MultipleOf = "multiple_of"); - -#[derive(Clone, Debug)] -pub(crate) struct Maximum(pub(crate) f64, pub(crate) Ident); -impl Validate for Maximum { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err( - Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`maximum` error: {}", error)).help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maximum`", - ), - ) - } else { - Ok(()) - } - } -} -impl Parse for Maximum { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|maximum| Self(maximum, ident)) - } -} -impl ToTokens for Maximum { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: Maximum) -> Self { - Feature::Maximum(value) - } -} -impl_get_name!(Maximum = "maximum"); - -#[derive(Clone, Debug)] -pub(crate) struct Minimum(pub(crate) f64, pub(crate) Ident); -impl Minimum { - pub(crate) fn new(value: f64, span: Span) -> Self { - Self(value, Ident::new("empty", span)) - } -} -impl Validate for Minimum { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err( - Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`minimum` error: {}", error)).help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minimum`", - ), - ) - } else { - Ok(()) - } - } -} -impl Parse for Minimum { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|maximum| Self(maximum, ident)) - } -} -impl ToTokens for Minimum { - fn to_tokens(&self, stream: &mut TokenStream) { - self.0.to_tokens(stream); - } -} -impl From for Feature { - fn from(value: Minimum) -> Self { - Feature::Minimum(value) - } -} -impl_get_name!(Minimum = "minimum"); - -#[derive(Clone, Debug)] -pub(crate) struct ExclusiveMaximum(pub(crate) f64, pub(crate) Ident); -impl Validate for ExclusiveMaximum { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err(Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`exclusive_maximum` error: {}", error)) - .help("See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusivemaximum`")) - } else { - Ok(()) - } - } -} -impl Parse for ExclusiveMaximum { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|max| Self(max, ident)) - } -} -impl ToTokens for ExclusiveMaximum { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: ExclusiveMaximum) -> Self { - Feature::ExclusiveMaximum(value) - } -} -impl_get_name!(ExclusiveMaximum = "exclusive_maximum"); - -#[derive(Clone, Debug)] -pub(crate) struct ExclusiveMinimum(pub(crate) f64, pub(crate) Ident); -impl Validate for ExclusiveMinimum { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err(Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`exclusive_minimum` error: {}", error)) - .help("See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusiveminimum`")) - } else { - Ok(()) - } - } -} -impl Parse for ExclusiveMinimum { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|min| Self(min, ident)) - } -} -impl ToTokens for ExclusiveMinimum { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: ExclusiveMinimum) -> Self { - Feature::ExclusiveMinimum(value) - } -} -impl_get_name!(ExclusiveMinimum = "exclusive_minimum"); - -#[derive(Clone, Debug)] -pub(crate) struct MaxLength(pub(crate) usize, pub(crate) Ident); -impl Validate for MaxLength { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err(Diagnostic::spanned( - self.1.span(), - DiagLevel::Error, - format!("`max_length` error: {}", error), - ) - .help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxlength`", - )) - } else { - Ok(()) - } - } -} -impl Parse for MaxLength { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_integer(input).map(|max_length| Self(max_length, ident)) - } -} -impl ToTokens for MaxLength { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MaxLength) -> Self { - Feature::MaxLength(value) - } -} -impl_get_name!(MaxLength = "max_length"); - -#[derive(Clone, Debug)] -pub(crate) struct MinLength(pub(crate) usize, pub(crate) Ident); -impl Validate for MinLength { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err(Diagnostic::spanned( - self.1.span(), - DiagLevel::Error, - format!("`min_length` error: {}", error), - ) - .help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minlength`", - )) - } else { - Ok(()) - } - } -} -impl Parse for MinLength { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_integer(input).map(|max_length| Self(max_length, ident)) - } -} -impl ToTokens for MinLength { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MinLength) -> Self { - Feature::MinLength(value) - } -} -impl_get_name!(MinLength = "min_length"); - -#[derive(Clone, Debug)] -pub(crate) struct Pattern(pub(crate) String, pub(crate) Ident); -impl Validate for Pattern { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err( - Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`pattern` error: {}", error)).help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-pattern`", - ), - ) - } else { - Ok(()) - } - } -} -impl Parse for Pattern { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_utils::parse_next(input, || input.parse::()) - .map(|pattern| Self(pattern.value(), ident)) - } -} -impl ToTokens for Pattern { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: Pattern) -> Self { - Feature::Pattern(value) - } -} -impl_get_name!(Pattern = "pattern"); - -#[derive(Clone, Debug)] -pub(crate) struct MaxItems(pub(crate) usize, pub(crate) Ident); -impl Validate for MaxItems { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err( - Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`max_items` error: {}", error)).help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxitems", - ), - ) - } else { - Ok(()) - } - } -} -impl Parse for MaxItems { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|max_items| Self(max_items, ident)) - } -} -impl ToTokens for MaxItems { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MaxItems) -> Self { - Feature::MaxItems(value) - } -} -impl_get_name!(MaxItems = "max_items"); - -#[derive(Clone, Debug)] -pub(crate) struct MinItems(pub(crate) usize, pub(crate) Ident); -impl Validate for MinItems { - fn validate(&self, validator: impl Validator) -> DiagResult<()> { - if let Err(error) = validator.is_valid() { - Err( - Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`min_items` error: {}", error)).help( - "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minitems", - ), - ) - } else { - Ok(()) - } - } -} -impl Parse for MinItems { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_number(input).map(|max_items| Self(max_items, ident)) - } -} -impl ToTokens for MinItems { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MinItems) -> Self { - Feature::MinItems(value) - } -} -impl_get_name!(MinItems = "min_items"); - -#[derive(Clone, Debug)] -#[allow(dead_code)] -pub(crate) struct MaxProperties(pub(crate) usize, pub(crate) Ident); -impl Parse for MaxProperties { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_integer(input).map(|max_properties| Self(max_properties, ident)) - } -} -impl ToTokens for MaxProperties { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MaxProperties) -> Self { - Feature::MaxProperties(value) - } -} -impl_get_name!(MaxProperties = "max_properties"); - -#[derive(Clone, Debug)] -#[allow(dead_code)] -pub(crate) struct MinProperties(pub(crate) usize, pub(crate) Ident); -impl Parse for MinProperties { - fn parse(input: ParseStream, ident: Ident) -> syn::Result - where - Self: Sized, - { - parse_integer(input).map(|min_properties| Self(min_properties, ident)) - } -} -impl ToTokens for MinProperties { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens); - } -} -impl From for Feature { - fn from(value: MinProperties) -> Self { - Feature::MinProperties(value) - } -} -impl_get_name!(MinProperties = "min_properties"); - #[derive(Clone, Debug)] pub(crate) struct SchemaWith(pub(crate) TypePath); impl Parse for SchemaWith { diff --git a/crates/oapi-macros/src/feature/ext.rs b/crates/oapi-macros/src/feature/ext.rs index f5dde4696..8159d0c05 100644 --- a/crates/oapi-macros/src/feature/ext.rs +++ b/crates/oapi-macros/src/feature/ext.rs @@ -1,12 +1,11 @@ use proc_macro2::TokenStream; -use crate::{ - feature::{Feature, Rename, RenameAll, Style, ValueType}, - type_tree::TypeTree, - DiagResult, TryToTokens, -}; +use crate::feature::attributes::{Rename, RenameAll, Style, ValueType}; +use crate::feature::Feature; +use crate::type_tree::TypeTree; +use crate::{DiagResult, TryToTokens}; -use super::ParameterIn; +use super::attributes::ParameterIn; pub(crate) trait TryToTokensExt { fn try_to_token_stream(&self) -> DiagResult; diff --git a/crates/oapi-macros/src/feature/macros.rs b/crates/oapi-macros/src/feature/macros.rs index 48430752e..180a7a300 100644 --- a/crates/oapi-macros/src/feature/macros.rs +++ b/crates/oapi-macros/src/feature/macros.rs @@ -1,4 +1,6 @@ -use crate::feature::{items::*, Feature, Validatable}; +use crate::feature::attributes::*; +use crate::feature::validation::*; +use crate::feature::{Feature, Validatable}; use crate::IntoInner; macro_rules! impl_get_name { diff --git a/crates/oapi-macros/src/feature/mod.rs b/crates/oapi-macros/src/feature/mod.rs index c2a528208..0470928ee 100644 --- a/crates/oapi-macros/src/feature/mod.rs +++ b/crates/oapi-macros/src/feature/mod.rs @@ -8,11 +8,16 @@ mod ext; pub(crate) use ext::*; mod macros; pub(crate) use macros::*; -mod items; -pub(crate) use items::*; +pub(crate) mod attributes; +pub(crate) mod validation; +pub(crate) mod validators; + +use crate::feature::attributes::*; +use crate::feature::validation::*; +use crate::feature::validators::*; use crate::schema_type::SchemaType; -use crate::type_tree::{GenericType, TypeTree}; +use crate::type_tree::TypeTree; use crate::{parse_utils, DiagLevel, DiagResult, Diagnostic, IntoInner, TryToTokens}; /// Parse `LitInt` from parse stream @@ -386,115 +391,6 @@ impl Validatable for Feature { } } -pub(crate) trait Validator { - fn is_valid(&self) -> Result<(), &'static str>; -} - -pub(crate) struct IsNumber<'a>(pub(crate) &'a SchemaType<'a>); - -impl Validator for IsNumber<'_> { - fn is_valid(&self) -> Result<(), &'static str> { - if self.0.is_number() { - Ok(()) - } else { - Err("can only be used with `number` type") - } - } -} - -pub(crate) struct IsString<'a>(&'a SchemaType<'a>); - -impl Validator for IsString<'_> { - fn is_valid(&self) -> Result<(), &'static str> { - if self.0.is_string() { - Ok(()) - } else { - Err("can only be used with `string` type") - } - } -} - -// pub(crate) struct IsInteger<'a>(&'a SchemaType<'a>); - -// impl Validator for IsInteger<'_> { -// fn is_valid(&self) -> Result<(), &'static str> { -// if self.0.is_integer() { -// Ok(()) -// } else { -// Err("can only be used with `integer` type") -// } -// } -// } - -pub(crate) struct IsVec<'a>(&'a TypeTree<'a>); - -impl Validator for IsVec<'_> { - fn is_valid(&self) -> Result<(), &'static str> { - if self.0.generic_type == Some(GenericType::Vec) { - Ok(()) - } else { - Err("can only be used with `Vec`, `array` or `slice` types") - } - } -} - -pub(crate) struct AboveZeroUsize(pub(crate) usize); - -impl Validator for AboveZeroUsize { - fn is_valid(&self) -> Result<(), &'static str> { - if self.0 != 0 { - Ok(()) - } else { - Err("can only be above zero value") - } - } -} - -pub(crate) struct AboveZeroF64(pub(crate) f64); - -impl Validator for AboveZeroF64 { - fn is_valid(&self) -> Result<(), &'static str> { - if self.0 > 0.0 { - Ok(()) - } else { - Err("can only be above zero value") - } - } -} - -pub(crate) struct ValidatorChain<'c> { - inner: &'c dyn Validator, - next: Option<&'c dyn Validator>, -} - -impl Validator for ValidatorChain<'_> { - fn is_valid(&self) -> Result<(), &'static str> { - self.inner.is_valid().and_then(|_| { - if let Some(validator) = self.next.as_ref() { - validator.is_valid() - } else { - // if there is no next validator consider it valid - Ok(()) - } - }) - } -} - -impl<'c> ValidatorChain<'c> { - pub(crate) fn new(validator: &'c dyn Validator) -> Self { - Self { - inner: validator, - next: None, - } - } - - pub(crate) fn next(mut self, validator: &'c dyn Validator) -> Self { - self.next = Some(validator); - - self - } -} - pub(crate) trait IsInline { fn is_inline(&self) -> bool; } diff --git a/crates/oapi-macros/src/feature/validation.rs b/crates/oapi-macros/src/feature/validation.rs new file mode 100644 index 000000000..ae618520a --- /dev/null +++ b/crates/oapi-macros/src/feature/validation.rs @@ -0,0 +1,411 @@ +use std::fmt::Display; + +use proc_macro2::{Ident, Span, TokenStream}; +use quote::ToTokens; +use syn::parse::ParseStream; +use syn::LitStr; + +use super::{impl_get_name, parse_integer, parse_number, Feature, Parse, Validate, Validator}; +use crate::{parse_utils, DiagLevel, DiagResult, Diagnostic}; + +#[derive(Clone, Debug)] +pub(crate) struct MultipleOf(pub(crate) f64, pub(crate) Ident); +impl Validate for MultipleOf { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err(Diagnostic::spanned( + self.1.span(), + DiagLevel::Error, + format!("`multiple_of` error: {}", error), + ) + .help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof`", + )) + } else { + Ok(()) + } + } +} +impl Parse for MultipleOf { + fn parse(input: ParseStream, ident: Ident) -> syn::Result { + parse_number(input).map(|multiple_of| Self(multiple_of, ident)) + } +} +impl ToTokens for MultipleOf { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MultipleOf) -> Self { + Feature::MultipleOf(value) + } +} +impl_get_name!(MultipleOf = "multiple_of"); + +#[derive(Clone, Debug)] +pub(crate) struct Maximum(pub(crate) f64, pub(crate) Ident); +impl Validate for Maximum { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err( + Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`maximum` error: {}", error)).help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maximum`", + ), + ) + } else { + Ok(()) + } + } +} +impl Parse for Maximum { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|maximum| Self(maximum, ident)) + } +} +impl ToTokens for Maximum { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: Maximum) -> Self { + Feature::Maximum(value) + } +} +impl_get_name!(Maximum = "maximum"); + +#[derive(Clone, Debug)] +pub(crate) struct Minimum(pub(crate) f64, pub(crate) Ident); +impl Minimum { + pub(crate) fn new(value: f64, span: Span) -> Self { + Self(value, Ident::new("empty", span)) + } +} +impl Validate for Minimum { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err( + Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`minimum` error: {}", error)).help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minimum`", + ), + ) + } else { + Ok(()) + } + } +} +impl Parse for Minimum { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|maximum| Self(maximum, ident)) + } +} +impl ToTokens for Minimum { + fn to_tokens(&self, stream: &mut TokenStream) { + self.0.to_tokens(stream); + } +} +impl From for Feature { + fn from(value: Minimum) -> Self { + Feature::Minimum(value) + } +} +impl_get_name!(Minimum = "minimum"); + +#[derive(Clone, Debug)] +pub(crate) struct ExclusiveMaximum(pub(crate) f64, pub(crate) Ident); +impl Validate for ExclusiveMaximum { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err(Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`exclusive_maximum` error: {}", error)) + .help("See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusivemaximum`")) + } else { + Ok(()) + } + } +} +impl Parse for ExclusiveMaximum { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|max| Self(max, ident)) + } +} +impl ToTokens for ExclusiveMaximum { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: ExclusiveMaximum) -> Self { + Feature::ExclusiveMaximum(value) + } +} +impl_get_name!(ExclusiveMaximum = "exclusive_maximum"); + +#[derive(Clone, Debug)] +pub(crate) struct ExclusiveMinimum(pub(crate) f64, pub(crate) Ident); +impl Validate for ExclusiveMinimum { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err(Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`exclusive_minimum` error: {}", error)) + .help("See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusiveminimum`")) + } else { + Ok(()) + } + } +} +impl Parse for ExclusiveMinimum { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|min| Self(min, ident)) + } +} +impl ToTokens for ExclusiveMinimum { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: ExclusiveMinimum) -> Self { + Feature::ExclusiveMinimum(value) + } +} +impl_get_name!(ExclusiveMinimum = "exclusive_minimum"); + +#[derive(Clone, Debug)] +pub(crate) struct MaxLength(pub(crate) usize, pub(crate) Ident); +impl Validate for MaxLength { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err(Diagnostic::spanned( + self.1.span(), + DiagLevel::Error, + format!("`max_length` error: {}", error), + ) + .help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxlength`", + )) + } else { + Ok(()) + } + } +} +impl Parse for MaxLength { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_integer(input).map(|max_length| Self(max_length, ident)) + } +} +impl ToTokens for MaxLength { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MaxLength) -> Self { + Feature::MaxLength(value) + } +} +impl_get_name!(MaxLength = "max_length"); + +#[derive(Clone, Debug)] +pub(crate) struct MinLength(pub(crate) usize, pub(crate) Ident); +impl Validate for MinLength { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err(Diagnostic::spanned( + self.1.span(), + DiagLevel::Error, + format!("`min_length` error: {}", error), + ) + .help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minlength`", + )) + } else { + Ok(()) + } + } +} +impl Parse for MinLength { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_integer(input).map(|max_length| Self(max_length, ident)) + } +} +impl ToTokens for MinLength { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MinLength) -> Self { + Feature::MinLength(value) + } +} +impl_get_name!(MinLength = "min_length"); + +#[derive(Clone, Debug)] +pub(crate) struct Pattern(pub(crate) String, pub(crate) Ident); +impl Validate for Pattern { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err( + Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`pattern` error: {}", error)).help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-pattern`", + ), + ) + } else { + Ok(()) + } + } +} +impl Parse for Pattern { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_utils::parse_next(input, || input.parse::()) + .map(|pattern| Self(pattern.value(), ident)) + } +} +impl ToTokens for Pattern { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: Pattern) -> Self { + Feature::Pattern(value) + } +} +impl_get_name!(Pattern = "pattern"); + +#[derive(Clone, Debug)] +pub(crate) struct MaxItems(pub(crate) usize, pub(crate) Ident); +impl Validate for MaxItems { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err( + Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`max_items` error: {}", error)).help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxitems", + ), + ) + } else { + Ok(()) + } + } +} +impl Parse for MaxItems { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|max_items| Self(max_items, ident)) + } +} +impl ToTokens for MaxItems { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MaxItems) -> Self { + Feature::MaxItems(value) + } +} +impl_get_name!(MaxItems = "max_items"); + +#[derive(Clone, Debug)] +pub(crate) struct MinItems(pub(crate) usize, pub(crate) Ident); +impl Validate for MinItems { + fn validate(&self, validator: impl Validator) -> DiagResult<()> { + if let Err(error) = validator.is_valid() { + Err( + Diagnostic::spanned(self.1.span(), DiagLevel::Error, format!("`min_items` error: {}", error)).help( + "See more details: `http://json-schema.org/draft/2020-12/json-schema-validation.html#name-minitems", + ), + ) + } else { + Ok(()) + } + } +} +impl Parse for MinItems { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_number(input).map(|max_items| Self(max_items, ident)) + } +} +impl ToTokens for MinItems { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MinItems) -> Self { + Feature::MinItems(value) + } +} +impl_get_name!(MinItems = "min_items"); + +#[derive(Clone, Debug)] +#[allow(dead_code)] +pub(crate) struct MaxProperties(pub(crate) usize, pub(crate) Ident); +impl Parse for MaxProperties { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_integer(input).map(|max_properties| Self(max_properties, ident)) + } +} +impl ToTokens for MaxProperties { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MaxProperties) -> Self { + Feature::MaxProperties(value) + } +} +impl_get_name!(MaxProperties = "max_properties"); + +#[derive(Clone, Debug)] +#[allow(dead_code)] +pub(crate) struct MinProperties(pub(crate) usize, pub(crate) Ident); +impl Parse for MinProperties { + fn parse(input: ParseStream, ident: Ident) -> syn::Result + where + Self: Sized, + { + parse_integer(input).map(|min_properties| Self(min_properties, ident)) + } +} +impl ToTokens for MinProperties { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} +impl From for Feature { + fn from(value: MinProperties) -> Self { + Feature::MinProperties(value) + } +} +impl_get_name!(MinProperties = "min_properties"); diff --git a/crates/oapi-macros/src/feature/validators.rs b/crates/oapi-macros/src/feature/validators.rs new file mode 100644 index 000000000..1a66e4a57 --- /dev/null +++ b/crates/oapi-macros/src/feature/validators.rs @@ -0,0 +1,111 @@ +use crate::schema_type::SchemaType; +use crate::type_tree::{GenericType, TypeTree}; + +pub(crate) trait Validator { + fn is_valid(&self) -> Result<(), &'static str>; +} + +pub(crate) struct IsNumber<'a>(pub(crate) &'a SchemaType<'a>); + +impl Validator for IsNumber<'_> { + fn is_valid(&self) -> Result<(), &'static str> { + if self.0.is_number() { + Ok(()) + } else { + Err("can only be used with `number` type") + } + } +} + +pub(crate) struct IsString<'a>(pub(crate) &'a SchemaType<'a>); + +impl Validator for IsString<'_> { + fn is_valid(&self) -> Result<(), &'static str> { + if self.0.is_string() { + Ok(()) + } else { + Err("can only be used with `string` type") + } + } +} + +// pub(crate) struct IsInteger<'a>(&'a SchemaType<'a>); + +// impl Validator for IsInteger<'_> { +// fn is_valid(&self) -> Result<(), &'static str> { +// if self.0.is_integer() { +// Ok(()) +// } else { +// Err("can only be used with `integer` type") +// } +// } +// } + +pub(crate) struct IsVec<'a>(pub(crate) &'a TypeTree<'a>); + +impl Validator for IsVec<'_> { + fn is_valid(&self) -> Result<(), &'static str> { + if self.0.generic_type == Some(GenericType::Vec) { + Ok(()) + } else { + Err("can only be used with `Vec`, `array` or `slice` types") + } + } +} + +pub(crate) struct AboveZeroUsize(pub(crate) usize); + +impl Validator for AboveZeroUsize { + fn is_valid(&self) -> Result<(), &'static str> { + if self.0 != 0 { + Ok(()) + } else { + Err("can only be above zero value") + } + } +} + +pub(crate) struct AboveZeroF64(pub(crate) f64); + +impl Validator for AboveZeroF64 { + fn is_valid(&self) -> Result<(), &'static str> { + if self.0 > 0.0 { + Ok(()) + } else { + Err("can only be above zero value") + } + } +} + +pub(crate) struct ValidatorChain<'c> { + inner: &'c dyn Validator, + next: Option<&'c dyn Validator>, +} + +impl Validator for ValidatorChain<'_> { + fn is_valid(&self) -> Result<(), &'static str> { + self.inner.is_valid().and_then(|_| { + if let Some(validator) = self.next.as_ref() { + validator.is_valid() + } else { + // if there is no next validator consider it valid + Ok(()) + } + }) + } +} + +impl<'c> ValidatorChain<'c> { + pub(crate) fn new(validator: &'c dyn Validator) -> Self { + Self { + inner: validator, + next: None, + } + } + + pub(crate) fn next(mut self, validator: &'c dyn Validator) -> Self { + self.next = Some(validator); + + self + } +} diff --git a/crates/oapi-macros/src/operation/request_body.rs b/crates/oapi-macros/src/operation/request_body.rs index 73272e0c2..df72e4dff 100644 --- a/crates/oapi-macros/src/operation/request_body.rs +++ b/crates/oapi-macros/src/operation/request_body.rs @@ -4,7 +4,7 @@ use syn::punctuated::Punctuated; use syn::{parenthesized, parse::Parse, token::Paren, Error, Token}; use crate::component::ComponentSchema; -use crate::feature::Inline; +use crate::feature::attributes::Inline; use crate::{parse_utils, AnyValue, Array, DiagResult, Required, TryToTokens}; use super::example::Example; diff --git a/crates/oapi-macros/src/parameter/derive.rs b/crates/oapi-macros/src/parameter/derive.rs index a7c3dfe8c..9e8fb0e51 100644 --- a/crates/oapi-macros/src/parameter/derive.rs +++ b/crates/oapi-macros/src/parameter/derive.rs @@ -10,12 +10,18 @@ use syn::{ use crate::component::{self, ComponentSchema}; use crate::doc_comment::CommentAttributes; +use crate::feature::attributes::{ + self, AdditionalProperties, AllowReserved, DefaultParameterIn, DefaultStyle, Example, Explode, + Format, Inline, Nullable, ReadOnly, Rename, RenameAll, SchemaWith, Style, ToParametersNames, + ValueType, WriteOnly, XmlAttr, +}; +use crate::feature::validation::{ + ExclusiveMaximum, ExclusiveMinimum, MaxItems, MaxLength, Maximum, MinItems, MinLength, Minimum, + MultipleOf, Pattern, +}; use crate::feature::{ - self, impl_into_inner, impl_merge, parse_features, pop_feature, AdditionalProperties, - AllowReserved, DefaultStyle, Example, ExclusiveMaximum, ExclusiveMinimum, Explode, Feature, - FeaturesExt, Format, Inline, MaxItems, MaxLength, Maximum, Merge, MinItems, MinLength, Minimum, - MultipleOf, Nullable, Pattern, ReadOnly, Rename, RenameAll, SchemaWith, Style, - ToParametersNames, TryToTokensExt, WriteOnly, XmlAttr, + impl_into_inner, impl_merge, parse_features, pop_feature, Feature, FeaturesExt, Merge, + TryToTokensExt, }; use crate::parameter::ParameterIn; use crate::serde_util::{self, RenameRule, SerdeContainer, SerdeValue}; @@ -34,7 +40,7 @@ impl Parse for ToParametersFeatures { fn parse(input: syn::parse::ParseStream) -> syn::Result { Ok(Self(parse_features!( input as DefaultStyle, - feature::DefaultParameterIn, + DefaultParameterIn, ToParametersNames, RenameAll ))) @@ -113,19 +119,19 @@ impl TryToTokens for ToParameters { let default_parameter_in = pop_feature!(parameters_features => Feature::DefaultParameterIn(_)); let rename_all = pop_feature!(parameters_features => Feature::RenameAll(_)); - let default_source_from = if let Some(Feature::DefaultParameterIn( - feature::DefaultParameterIn(default_parameter_in), - )) = default_parameter_in - { - match default_parameter_in { - ParameterIn::Query => quote! { #salvo::extract::metadata::SourceFrom::Query }, - ParameterIn::Header => quote! { #salvo::extract::metadata::SourceFrom::Header }, - ParameterIn::Path => quote! { #salvo::extract::metadata::SourceFrom::Param }, - ParameterIn::Cookie => quote! { #salvo::extract::metadata::SourceFrom::Cookie }, - } - } else { - quote! { #salvo::extract::metadata::SourceFrom::Query } - }; + let default_source_from = + if let Some(Feature::DefaultParameterIn(DefaultParameterIn(default_parameter_in))) = + default_parameter_in + { + match default_parameter_in { + ParameterIn::Query => quote! { #salvo::extract::metadata::SourceFrom::Query }, + ParameterIn::Header => quote! { #salvo::extract::metadata::SourceFrom::Header }, + ParameterIn::Path => quote! { #salvo::extract::metadata::SourceFrom::Param }, + ParameterIn::Cookie => quote! { #salvo::extract::metadata::SourceFrom::Cookie }, + } + } else { + quote! { #salvo::extract::metadata::SourceFrom::Query } + }; let default_source = quote! { #salvo::extract::metadata::Source::new(#default_source_from, #salvo::extract::metadata::SourceParser::MultiMap) }; let params = self .get_struct_fields(&names.as_ref())? @@ -353,19 +359,19 @@ impl Parse for FieldFeatures { fn parse(input: syn::parse::ParseStream) -> syn::Result { Ok(Self(parse_features!( // param features - input as feature::ValueType, + input as ValueType, Rename, Style, - feature::ParameterIn, + attributes::ParameterIn, AllowReserved, Example, Explode, SchemaWith, - feature::Required, + attributes::Required, // param schema features Inline, Format, - feature::Default, + attributes::Default, WriteOnly, ReadOnly, Nullable, @@ -482,16 +488,16 @@ impl Parameter<'_> { }); if let Some(parameter_in) = param_features.pop_parameter_in_feature() { let source = match parameter_in { - feature::ParameterIn(crate::parameter::ParameterIn::Query) => { + attributes::ParameterIn(crate::parameter::ParameterIn::Query) => { quote! { #salvo::extract::metadata::Source::new(#salvo::extract::metadata::SourceFrom::Query, #salvo::extract::metadata::SourceParser::Smart) } } - feature::ParameterIn(crate::parameter::ParameterIn::Header) => { + attributes::ParameterIn(crate::parameter::ParameterIn::Header) => { quote! { #salvo::extract::metadata::Source::new(#salvo::extract::metadata::SourceFrom::Header, #salvo::extract::metadata::SourceParser::Smart) } } - feature::ParameterIn(crate::parameter::ParameterIn::Path) => { + attributes::ParameterIn(crate::parameter::ParameterIn::Path) => { quote! { #salvo::extract::metadata::Source::new(#salvo::extract::metadata::SourceFrom::Param, #salvo::extract::metadata::SourceParser::Smart) } } - feature::ParameterIn(crate::parameter::ParameterIn::Cookie) => { + attributes::ParameterIn(crate::parameter::ParameterIn::Cookie) => { quote! { #salvo::extract::metadata::Source::new(#salvo::extract::metadata::SourceFrom::Cookie, #salvo::extract::metadata::SourceParser::Smart) } } }; @@ -593,7 +599,7 @@ impl TryToTokens for Parameter<'_> { .transpose()? .unwrap_or(type_tree); - let required: Option = + let required: Option = pop_feature!(param_features => Feature::Required(_)).into_inner(); let component_required = !component.is_option() && crate::is_required(self.field_serde_params.as_ref(), self.serde_container); diff --git a/crates/oapi-macros/src/parameter/mod.rs b/crates/oapi-macros/src/parameter/mod.rs index c9ec7c392..15e14fa9a 100644 --- a/crates/oapi-macros/src/parameter/mod.rs +++ b/crates/oapi-macros/src/parameter/mod.rs @@ -9,16 +9,17 @@ use syn::{DeriveInput, Error, ExprPath, LitStr, Token}; mod derive; use derive::ToParameters; -use crate::{ - component::{self, ComponentSchema}, - feature::{ - parse_features, AllowReserved, Description, Example, ExclusiveMaximum, ExclusiveMinimum, - Explode, Feature, Format, MaxItems, MaxLength, Maximum, MinItems, MinLength, Minimum, - MultipleOf, Nullable, Pattern, ReadOnly, Style, TryToTokensExt, WriteOnly, XmlAttr, - }, - operation::InlineType, - parse_utils, Required, +use crate::component::{self, ComponentSchema}; +use crate::feature::attributes::{ + AllowReserved, Deprecated, Description, Example, Explode, Format, Nullable, ReadOnly, Style, + WriteOnly, XmlAttr, }; +use crate::feature::validation::{ + ExclusiveMaximum, ExclusiveMinimum, MaxItems, MaxLength, Maximum, MinItems, MinLength, Minimum, + MultipleOf, Pattern, +}; +use crate::feature::{parse_features, Feature, TryToTokensExt}; +use crate::{operation::InlineType, parse_utils, Required}; use crate::{DiagLevel, DiagResult, Diagnostic, TryToTokens}; pub(crate) fn to_parameters(input: DeriveInput) -> DiagResult { @@ -210,7 +211,7 @@ impl Parse for ParameterFeatures { Explode, AllowReserved, Example, - crate::feature::Deprecated, + Deprecated, Description, // param schema features Format, diff --git a/crates/oapi-macros/src/response/mod.rs b/crates/oapi-macros/src/response/mod.rs index d7ca52bf6..a6b60ab33 100644 --- a/crates/oapi-macros/src/response/mod.rs +++ b/crates/oapi-macros/src/response/mod.rs @@ -8,7 +8,7 @@ use syn::spanned::Spanned; use syn::{parenthesized, Attribute, DeriveInput, Error, ExprPath, LitInt, LitStr, Token}; use crate::component::ComponentSchema; -use crate::feature::Inline; +use crate::feature::attributes::Inline; use crate::operation::{ example::Example, status::STATUS_CODES, InlineType, PathType, PathTypeTree, }; diff --git a/crates/oapi-macros/src/schema/enum_schemas.rs b/crates/oapi-macros/src/schema/enum_schemas.rs index 547225af8..0fb0fd9fa 100644 --- a/crates/oapi-macros/src/schema/enum_schemas.rs +++ b/crates/oapi-macros/src/schema/enum_schemas.rs @@ -9,9 +9,12 @@ use syn::{Attribute, Fields, Generics, Token, Variant}; use crate::component::ComponentDescription; use crate::doc_comment::CommentAttributes; +use crate::feature::attributes::{ + Alias, Bound, Example, Name, Rename, RenameAll, SkipBound, Title, +}; use crate::feature::{ - parse_features, pop_feature, pop_feature_as_inner, Alias, Bound, Example, Feature, FeaturesExt, - IsSkipped, Name, Rename, RenameAll, SkipBound, TryToTokensExt, + parse_features, pop_feature, pop_feature_as_inner, Feature, FeaturesExt, IsSkipped, + TryToTokensExt, }; use crate::schema::{Description, Inline, VariantRename}; use crate::serde_util::{self, SerdeContainer, SerdeEnumRepr, SerdeValue}; @@ -65,12 +68,12 @@ impl<'e> EnumSchema<'e> { let mut repr_enum_features = feature::parse_schema_features_with(attributes, |input| { Ok(parse_features!( - input as crate::feature::Example, - crate::feature::Examples, - crate::feature::Default, - crate::feature::Name, - crate::feature::Title, - crate::feature::Inline + input as Example, + crate::feature::attributes::Examples, + crate::feature::attributes::Default, + Name, + Title, + crate::feature::attributes::Inline )) })? .unwrap_or_default(); @@ -579,12 +582,7 @@ impl ComplexEnum<'_> { Fields::Unit => { let mut unit_features = feature::parse_schema_features_with(&variant.attrs, |input| { - Ok(parse_features!( - input as crate::feature::Title, - RenameAll, - Rename, - Example - )) + Ok(parse_features!(input as Title, RenameAll, Rename, Example)) })? .unwrap_or_default(); @@ -684,7 +682,7 @@ impl ComplexEnum<'_> { Fields::Unit => { let mut unit_features = feature::parse_schema_features_with(&variant.attrs, |input| { - Ok(parse_features!(input as crate::feature::Title)) + Ok(parse_features!(input as Title)) })? .unwrap_or_default(); @@ -844,7 +842,7 @@ impl ComplexEnum<'_> { Fields::Unit => { let mut unit_features = feature::parse_schema_features_with(&variant.attrs, |input| { - Ok(parse_features!(input as crate::feature::Title, Rename)) + Ok(parse_features!(input as Title, Rename)) })? .unwrap_or_default(); @@ -1013,7 +1011,7 @@ impl ComplexEnum<'_> { // In this case `content` is simply ignored - there is nothing to put in it. let mut unit_features = feature::parse_schema_features_with(&variant.attrs, |input| { - Ok(parse_features!(input as crate::feature::Title, Rename)) + Ok(parse_features!(input as Title, Rename)) })? .unwrap_or_default(); diff --git a/crates/oapi-macros/src/schema/feature.rs b/crates/oapi-macros/src/schema/feature.rs index 89791dcff..c0001cbcb 100644 --- a/crates/oapi-macros/src/schema/feature.rs +++ b/crates/oapi-macros/src/schema/feature.rs @@ -1,14 +1,16 @@ use syn::parse::{Parse, ParseBuffer, ParseStream}; use syn::Attribute; -use crate::feature::{ - impl_into_inner, impl_merge, parse_features, AdditionalProperties, Aliases, Bound, - ContentEncoding, ContentMediaType, Default, Deprecated, Description, Example, Examples, - ExclusiveMaximum, ExclusiveMinimum, Feature, Format, Inline, MaxItems, MaxLength, - MaxProperties, Maximum, Merge, MinItems, MinLength, MinProperties, Minimum, MultipleOf, Name, - Nullable, Pattern, ReadOnly, Rename, RenameAll, Required, SchemaWith, Skip, SkipBound, Title, - ValueType, WriteOnly, XmlAttr, +use crate::feature::attributes::{ + AdditionalProperties, Aliases, Bound, ContentEncoding, ContentMediaType, Default, Deprecated, + Description, Example, Examples, Format, Inline, Name, Nullable, ReadOnly, Rename, RenameAll, + Required, SchemaWith, Skip, SkipBound, Title, ValueType, WriteOnly, XmlAttr, }; +use crate::feature::validation::{ + ExclusiveMaximum, ExclusiveMinimum, MaxItems, MaxLength, MaxProperties, Maximum, MinItems, + MinLength, MinProperties, Minimum, MultipleOf, Pattern, +}; +use crate::feature::{impl_into_inner, impl_merge, parse_features, Feature, Merge}; use crate::{attribute, DiagResult, Diagnostic, IntoInner}; #[derive(Debug)] diff --git a/crates/oapi-macros/src/schema/mod.rs b/crates/oapi-macros/src/schema/mod.rs index b8c3f5ca8..4215de261 100644 --- a/crates/oapi-macros/src/schema/mod.rs +++ b/crates/oapi-macros/src/schema/mod.rs @@ -25,10 +25,8 @@ pub(crate) use self::{ }; use super::{ComponentSchema, FieldRename, VariantRename}; -use crate::feature::{ - pop_feature, pop_feature_as_inner, Alias, Bound, Description, Feature, FeaturesExt, Inline, - Name, SkipBound, -}; +use crate::feature::attributes::{Alias, Bound, Description, Inline, Name, SkipBound}; +use crate::feature::{pop_feature, pop_feature_as_inner, Feature, FeaturesExt}; use crate::schema::feature::EnumFeatures; use crate::serde_util::SerdeValue; use crate::{bound, DiagLevel, DiagResult, Diagnostic, IntoInner, TryToTokens}; diff --git a/crates/oapi-macros/src/schema/struct_schemas.rs b/crates/oapi-macros/src/schema/struct_schemas.rs index a299555bb..856334f58 100644 --- a/crates/oapi-macros/src/schema/struct_schemas.rs +++ b/crates/oapi-macros/src/schema/struct_schemas.rs @@ -7,9 +7,12 @@ use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Field, Generics, use crate::component::{ComponentDescription, ComponentSchemaProps}; use crate::doc_comment::CommentAttributes; +use crate::feature::attributes::{ + self, Alias, Bound, Default, Name, RenameAll, Required, SkipBound, +}; use crate::feature::{ - parse_features, pop_feature, pop_feature_as_inner, Alias, Bound, Feature, FeaturesExt, - IsSkipped, Name, RenameAll, SkipBound, TryToTokensExt, + parse_features, pop_feature, pop_feature_as_inner, Feature, FeaturesExt, IsSkipped, + TryToTokensExt, }; use crate::schema::{Description, Inline}; use crate::type_tree::TypeTree; @@ -41,7 +44,7 @@ pub(crate) struct NamedStructSchema<'a> { struct NamedStructFieldOptions<'a> { property: Property, rename_field_value: Option>, - required: Option, + required: Option, is_option: bool, } @@ -87,9 +90,10 @@ impl NamedStructSchema<'_> { .expect("field ident shoule be exist") .to_owned(); let struct_ident = format_ident!("{}", &self.struct_name); - features_inner.push(Feature::Default( - crate::feature::Default::new_default_trait(struct_ident, field_ident.into()), - )); + features_inner.push(Feature::Default(Default::new_default_trait( + struct_ident, + field_ident.into(), + ))); } } @@ -381,20 +385,19 @@ impl TryToTokens for UnnamedStructSchema<'_> { if fields_len == 1 { if let Some(ref mut features) = unnamed_struct_features { let inline = parse_schema_features_with(&first_field.attrs, |input| { - Ok(parse_features!(input as crate::feature::Inline)) + Ok(parse_features!(input as attributes::Inline)) })? .unwrap_or_default(); features.extend(inline); - if pop_feature!(features => Feature::Default(crate::feature::Default(None))) - .is_some() - { + if pop_feature!(features => Feature::Default(Default(None))).is_some() { let struct_ident = format_ident!("{}", &self.struct_name); let index: syn::Index = 0.into(); - features.push(Feature::Default( - crate::feature::Default::new_default_trait(struct_ident, index.into()), - )); + features.push(Feature::Default(Default::new_default_trait( + struct_ident, + index.into(), + ))); } } } diff --git a/crates/oapi-macros/src/shared.rs b/crates/oapi-macros/src/shared.rs index 6a579b1f2..b88801098 100644 --- a/crates/oapi-macros/src/shared.rs +++ b/crates/oapi-macros/src/shared.rs @@ -13,7 +13,8 @@ use syn::{ TypePath, }; -use crate::{feature, parse_utils, RenameRule, SerdeContainer, SerdeValue}; +use crate::feature::attributes; +use crate::{parse_utils, RenameRule, SerdeContainer, SerdeValue}; #[allow(dead_code)] pub(crate) enum InputType<'a> { @@ -259,9 +260,9 @@ impl From for Required { } } -impl From for Required { - fn from(value: feature::Required) -> Self { - let feature::Required(required) = value; +impl From for Required { + fn from(value: attributes::Required) -> Self { + let attributes::Required(required) = value; crate::Required::from(required) } }