Skip to content

Commit

Permalink
oapi:Add parsing support for non strict integers (#896)
Browse files Browse the repository at this point in the history
* oapi:Add parsing support for non strict integers

* fix fmt

* fix bug

* fix test

* fix test

* fix warning

* fix ci
  • Loading branch information
chrislearn committed Sep 10, 2024
1 parent 8c7d371 commit 390a96b
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 34 deletions.
1 change: 1 addition & 0 deletions crates/oapi-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ time = []
smallvec = []
repr = []
indexmap = []
non-strict-integers = []

[dependencies]
proc-macro2 = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/oapi-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ mod tests {
"high",
salvo::oapi::Object::new()
.schema_type(salvo::oapi::schema::SchemaType::basic(salvo::oapi::schema::BasicType::Integer))
.format(salvo::oapi::SchemaFormat::KnownFormat(salvo::oapi::KnownFormat::Int32))
.format(salvo::oapi::SchemaFormat::KnownFormat(salvo::oapi::KnownFormat::UInt32))
.deprecated(salvo::oapi::Deprecated::True)
.minimum(0f64)
)
Expand Down
119 changes: 98 additions & 21 deletions crates/oapi-macros/src/schema_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,14 +402,33 @@ impl TryToTokens for Type<'_> {
let name = &*last_segment.ident.to_string();

match name {
"i8" | "i16" | "i32" | "u8" | "u16" | "u32" => {
#[cfg(feature="non-strict-integers")]
"i8" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int8) }),
#[cfg(feature="non-strict-integers")]
"u8" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::UInt8) }),
#[cfg(feature="non-strict-integers")]
"i16" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int16) }),
#[cfg(feature="non-strict-integers")]
"u16" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::UInt16) }),
#[cfg(feature="non-strict-integers")]
#[cfg(feature="non-strict-integers")]
"u32" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::UInt32) }),
#[cfg(feature="non-strict-integers")]
"u64" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::UInt64) }),

#[cfg(not(feature="non-strict-integers"))]
"i8" | "i16" | "u8" | "u16" | "u32" => {
tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int32) })
}
"i64" | "u64" => {
tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int64) })
}

#[cfg(not(feature="non-strict-integers"))]
"u64" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int64) }),

"i32" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int32) }),
"i64" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Int64) }),
"f32" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Float) }),
"f64" => tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Double) }),

#[cfg(any(feature = "decimal", feature = "decimal-float"))]
"Decimal" => {
tokens.extend(quote! { #oapi::oapi::SchemaFormat::KnownFormat(#oapi::oapi::KnownFormat::Decimal) })
Expand Down Expand Up @@ -448,8 +467,20 @@ impl TryToTokens for Type<'_> {
/// [`Parse`] and [`ToTokens`] implementation for [`salvo_oapi::schema::SchemaFormat`].
#[derive(Clone, Debug)]
pub(crate) enum Variant {
#[cfg(feature = "non-strict-integers")]
Int8,
#[cfg(feature = "non-strict-integers")]
Int16,
Int32,
Int64,
#[cfg(feature = "non-strict-integers")]
UInt8,
#[cfg(feature = "non-strict-integers")]
UInt16,
#[cfg(feature = "non-strict-integers")]
UInt32,
#[cfg(feature = "non-strict-integers")]
UInt64,
Float,
Double,
Byte,
Expand All @@ -468,31 +499,63 @@ pub(crate) enum Variant {

impl Parse for Variant {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const FORMATS: [&str; 12] = [
"Int32", "Int64", "Float", "Double", "Byte", "Binary", "Date", "DateTime", "Password",
"Ulid", "Uuid", "Url",
];
let excluded_format: &[&str] = &[
#[cfg(not(feature = "url"))]
"Uri",
#[cfg(not(feature = "uuid"))]
let default_formats = [
"Int32",
"Int64",
"Float",
"Double",
"Byte",
"Binary",
"Date",
"DateTime",
"Password",
#[cfg(feature = "uuid")]
"Uuid",
#[cfg(not(feature = "ulid"))]
#[cfg(feature = "ulid")]
"Ulid",
#[cfg(feature = "url")]
"Uri",
];
#[cfg(feature = "non-strict-integers")]
let non_strict_integer_formats = [
"Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64",
];
let known_formats = FORMATS
.into_iter()
.filter(|format| !excluded_format.contains(format))
.collect::<Vec<_>>();

#[cfg(feature = "non-strict-integers")]
let formats = {
let mut formats = default_formats
.into_iter()
.chain(non_strict_integer_formats)
.collect::<Vec<_>>();
formats.sort_unstable();
formats.join(", ")
};
#[cfg(not(feature = "non-strict-integers"))]
let formats = {
let formats = default_formats.into_iter().collect::<Vec<_>>();
formats.join(", ")
};

let lookahead = input.lookahead1();
if lookahead.peek(Ident) {
let format = input.parse::<Ident>()?;
let name = &*format.to_string();

match name {
#[cfg(feature = "non-strict-integers")]
"Int8" => Ok(Self::Int8),
#[cfg(feature = "non-strict-integers")]
"Int16" => Ok(Self::Int16),
"Int32" => Ok(Self::Int32),
"Int64" => Ok(Self::Int64),
#[cfg(feature = "non-strict-integers")]
"UInt8" => Ok(Self::UInt8),
#[cfg(feature = "non-strict-integers")]
"UInt16" => Ok(Self::UInt16),
#[cfg(feature = "non-strict-integers")]
"UInt32" => Ok(Self::UInt32),
#[cfg(feature = "non-strict-integers")]
"UInt64" => Ok(Self::UInt64),
"Float" => Ok(Self::Float),
"Double" => Ok(Self::Double),
"Byte" => Ok(Self::Byte),
Expand All @@ -508,10 +571,7 @@ impl Parse for Variant {
"Ulid" => Ok(Self::Ulid),
_ => Err(Error::new(
format.span(),
format!(
"unexpected format: {name}, expected one of: {}",
known_formats.join(", ")
),
format!("unexpected format: {name}, expected one of: {formats}"),
)),
}
} else if lookahead.peek(LitStr) {
Expand All @@ -527,12 +587,29 @@ impl ToTokens for Variant {
fn to_tokens(&self, tokens: &mut TokenStream) {
let oapi = crate::oapi_crate();
match self {
#[cfg(feature = "non-strict-integers")]
Self::Int8 => tokens.extend(quote! {#oapi::oapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int8)}),
#[cfg(feature = "non-strict-integers")]
Self::Int16 => tokens.extend(quote! {#oapi::oapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int16)}),

Self::Int32 => tokens.extend(quote!(#oapi::oapi::SchemaFormat::KnownFormat(
#oapi::oapi::KnownFormat::Int32
))),
Self::Int64 => tokens.extend(quote!(#oapi::oapi::SchemaFormat::KnownFormat(
#oapi::oapi::KnownFormat::Int64
))),
#[cfg(feature = "non-strict-integers")]
Self::UInt8 => tokens.extend(quote! {#oapi::oapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt8)}),
#[cfg(feature = "non-strict-integers")]
Self::UInt16 => tokens.extend(quote! {#oapi::oapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt16)}),
#[cfg(feature = "non-strict-integers")]
Self::UInt32 => tokens.extend(quote!(#oapi::oapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::UInt32
))),
#[cfg(feature = "non-strict-integers")]
Self::UInt64 => tokens.extend(quote!(#oapi::oapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::UInt64
))),
Self::Float => tokens.extend(quote!(#oapi::oapi::SchemaFormat::KnownFormat(
#oapi::oapi::KnownFormat::Float
))),
Expand Down
2 changes: 1 addition & 1 deletion crates/oapi-macros/src/type_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<'t> TypeTree<'t> {
Self::from_type_paths(Self::get_type_paths(ty)?)
}

fn get_type_paths(ty: &'t Type) -> DiagResult<Vec<TypeTreeValue>> {
fn get_type_paths(ty: &Type) -> DiagResult<Vec<TypeTreeValue>> {
let type_tree_values = match ty {
Type::Path(path) => {
vec![TypeTreeValue::TypePath(path)]
Expand Down
2 changes: 1 addition & 1 deletion crates/oapi-macros/tests/derive_to_schema_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn test_derive_to_schema_generics() {
"properties": {
"value": {
"type": "integer",
"format": "int64",
"format": "uint64",
"minimum": 0.0
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/oapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ authors = ["Juha Kukkonen <[email protected]>", "Chrislearn Young <chrisle

[features]
default = []
full = ["swagger-ui", "scalar", "rapidoc", "redoc", "chrono", "decimal", "url", "ulid", "uuid", "time", "smallvec", "indexmap", "yaml"]
full = ["swagger-ui", "scalar", "rapidoc", "redoc", "chrono", "decimal", "url", "ulid", "uuid", "time", "smallvec", "indexmap", "yaml", "non-strict-integers"]
swagger-ui = ["dep:rust-embed"]
scalar = []
rapidoc = []
redoc = []
chrono = ["salvo-oapi-macros/chrono", "dep:chrono"]
decimal = ["salvo-oapi-macros/decimal", "dep:rust_decimal"]
decimal-float = ["salvo-oapi-macros/decimal-float", "dep:rust_decimal"]
non-strict-integers = ["salvo-oapi-macros/non-strict-integers"]
url = ["salvo-oapi-macros/url", "dep:url"]
ulid = ["salvo-oapi-macros/ulid", "dep:ulid"]
uuid = ["salvo-oapi-macros/uuid", "dep:uuid"]
Expand Down
12 changes: 6 additions & 6 deletions crates/oapi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,12 @@ mod tests {
(
"i8",
i8::to_schema(&mut components),
json!({"type": "integer", "format": "int32"}),
json!({"type": "integer", "format": "int8"}),
),
(
"i16",
i16::to_schema(&mut components),
json!({"type": "integer", "format": "int32"}),
json!({"type": "integer", "format": "int16"}),
),
(
"i32",
Expand All @@ -661,22 +661,22 @@ mod tests {
(
"u8",
u8::to_schema(&mut components),
json!({"type": "integer", "format": "int32", "minimum": 0.0}),
json!({"type": "integer", "format": "uint8", "minimum": 0.0}),
),
(
"u16",
u16::to_schema(&mut components),
json!({"type": "integer", "format": "int32", "minimum": 0.0}),
json!({"type": "integer", "format": "uint16", "minimum": 0.0}),
),
(
"u32",
u32::to_schema(&mut components),
json!({"type": "integer", "format": "int32", "minimum": 0.0}),
json!({"type": "integer", "format": "uint32", "minimum": 0.0}),
),
(
"u64",
u64::to_schema(&mut components),
json!({"type": "integer", "format": "int64", "minimum": 0.0}),
json!({"type": "integer", "format": "uint64", "minimum": 0.0}),
),
(
"u128",
Expand Down
6 changes: 3 additions & 3 deletions crates/oapi/src/openapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ mod tests {
"required": true,
"schema": {
"type": "integer",
"format": "int64",
"format": "uint64",
"minimum": 0.0
}
},
Expand Down Expand Up @@ -1098,7 +1098,7 @@ mod tests {
},
"id": {
"type": "integer",
"format": "int64",
"format": "uint64",
"minimum": 0.0
},
"name": {
Expand Down Expand Up @@ -1448,7 +1448,7 @@ mod tests {
},
"code": {
"type": "integer",
"format": "int32",
"format": "uint16",
"minimum": 0.0
},
"detail": {
Expand Down
4 changes: 4 additions & 0 deletions crates/oapi/src/openapi/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,12 +484,16 @@ pub enum KnownFormat {
/// 64 bit integer.
Int64,
/// 8 bit unsigned integer.
#[serde(rename = "uint8")]
UInt8,
/// 16 bit unsigned integer.
#[serde(rename = "uint16")]
UInt16,
/// 32 bit unsigned integer.
#[serde(rename = "uint32")]
UInt32,
/// 64 bit unsigned integer.
#[serde(rename = "uint64")]
UInt64,
/// floating point number.
Float,
Expand Down

0 comments on commit 390a96b

Please sign in to comment.