Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing formats for KnownFormat parsing #1178

Merged
merged 1 commit into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions utoipa-gen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog - utoipa-gen

## Unreleased

### Changed

* Added missing formats for `KnownFormat` parsing (https://github.com/juhaku/utoipa/pull/1178)

## 5.1.3 - Oct 27 2024

### Fixed
Expand Down
178 changes: 140 additions & 38 deletions utoipa-gen/src/schema_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,17 +326,34 @@ pub enum KnownFormat {
Binary,
Date,
DateTime,
Duration,
Password,
#[cfg(feature = "uuid")]
Uuid,
#[cfg(feature = "ulid")]
Ulid,
#[cfg(feature = "url")]
Uri,
#[cfg(feature = "url")]
UriReference,
#[cfg(feature = "url")]
Iri,
#[cfg(feature = "url")]
IriReference,
Email,
IdnEmail,
Hostname,
IdnHostname,
Ipv4,
Ipv6,
UriTemplate,
JsonPointer,
RelativeJsonPointer,
Regex,
/// Custom format is reserved only for manual entry.
Custom(String),
/// This is not really tokenized, but is actually only for purpose of having some format in
/// case we do not know what the format is actually.
/// This is not tokenized, but is present for purpose of having some format in
/// case we do not know the format. E.g. We cannot determine the format based on type path.
#[allow(unused)]
Unknown,
}
Expand Down Expand Up @@ -408,10 +425,8 @@ impl KnownFormat {
pub fn is_known_format(&self) -> bool {
!matches!(self, Self::Unknown)
}
}

impl Parse for KnownFormat {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
fn get_allowed_formats() -> String {
let default_formats = [
"Int32",
"Int64",
Expand All @@ -421,13 +436,30 @@ impl Parse for KnownFormat {
"Binary",
"Date",
"DateTime",
"Duration",
"Password",
#[cfg(feature = "uuid")]
"Uuid",
#[cfg(feature = "ulid")]
"Ulid",
#[cfg(feature = "url")]
"Uri",
#[cfg(feature = "url")]
"UriReference",
#[cfg(feature = "url")]
"Iri",
#[cfg(feature = "url")]
"IriReference",
"Email",
"IdnEmail",
"Hostname",
"IdnHostname",
"Ipv4",
"Ipv6",
"UriTemplate",
"JsonPointer",
"RelativeJsonPointer",
"Regex",
];
#[cfg(feature = "non_strict_integers")]
let non_strict_integer_formats = [
Expand All @@ -449,6 +481,14 @@ impl Parse for KnownFormat {
formats.join(", ")
};

formats
}
}

impl Parse for KnownFormat {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let formats = KnownFormat::get_allowed_formats();

let lookahead = input.lookahead1();
if lookahead.peek(Ident) {
let format = input.parse::<Ident>()?;
Expand All @@ -475,13 +515,30 @@ impl Parse for KnownFormat {
"Binary" => Ok(Self::Binary),
"Date" => Ok(Self::Date),
"DateTime" => Ok(Self::DateTime),
"Duration" => Ok(Self::Duration),
"Password" => Ok(Self::Password),
#[cfg(feature = "uuid")]
"Uuid" => Ok(Self::Uuid),
#[cfg(feature = "ulid")]
"Ulid" => Ok(Self::Ulid),
#[cfg(feature = "url")]
"Uri" => Ok(Self::Uri),
#[cfg(feature = "url")]
"UriReference" => Ok(Self::UriReference),
#[cfg(feature = "url")]
"Iri" => Ok(Self::Iri),
#[cfg(feature = "url")]
"IriReference" => Ok(Self::IriReference),
"Email" => Ok(Self::Email),
"IdnEmail" => Ok(Self::IdnEmail),
"Hostname" => Ok(Self::Hostname),
"IdnHostname" => Ok(Self::IdnHostname),
"Ipv4" => Ok(Self::Ipv4),
"Ipv6" => Ok(Self::Ipv6),
"UriTemplate" => Ok(Self::UriTemplate),
"JsonPointer" => Ok(Self::JsonPointer),
"RelativeJsonPointer" => Ok(Self::RelativeJsonPointer),
"Regex" => Ok(Self::Regex),
_ => Err(Error::new(
format.span(),
format!("unexpected format: {name}, expected one of: {formats}"),
Expand All @@ -500,61 +557,106 @@ impl ToTokens for KnownFormat {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
#[cfg(feature = "non_strict_integers")]
Self::Int8 => tokens.extend(quote! {utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int8)}),
Self::Int8 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::Int8)}),
#[cfg(feature = "non_strict_integers")]
Self::Int16 => tokens.extend(quote! {utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int16)}),
Self::Int32 => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Int32
Self::Int16 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::Int16)}),
Self::Int32 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Int32
))),
Self::Int64 => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Int64
Self::Int64 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Int64
))),
#[cfg(feature = "non_strict_integers")]
Self::UInt8 => tokens.extend(quote! {utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt8)}),
Self::UInt8 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::UInt8)}),
#[cfg(feature = "non_strict_integers")]
Self::UInt16 => tokens.extend(quote! {utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::UInt16)}),
Self::UInt16 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::UInt16)}),
#[cfg(feature = "non_strict_integers")]
Self::UInt32 => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::UInt32
Self::UInt32 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::UInt32
))),
#[cfg(feature = "non_strict_integers")]
Self::UInt64 => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::UInt64
Self::UInt64 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::UInt64
))),
Self::Float => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Float
Self::Float => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Float
))),
Self::Double => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Double
Self::Double => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Double
))),
Self::Byte => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Byte
Self::Byte => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Byte
))),
Self::Binary => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Binary
Self::Binary => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Binary
))),
Self::Date => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Date
Self::Date => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Date
))),
Self::DateTime => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::DateTime
Self::DateTime => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::DateTime
))),
Self::Password => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Password
Self::Duration => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Duration
) }),
Self::Password => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Password
))),
#[cfg(feature = "uuid")]
Self::Uuid => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Uuid
Self::Uuid => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Uuid
))),
#[cfg(feature = "ulid")]
Self::Ulid => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Ulid
Self::Ulid => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Ulid
))),
#[cfg(feature = "url")]
Self::Uri => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Uri
))),
#[cfg(feature = "url")]
Self::UriReference => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::UriReference
))),
#[cfg(feature = "url")]
Self::Uri => tokens.extend(quote!(utoipa::openapi::SchemaFormat::KnownFormat(
utoipa::openapi::KnownFormat::Uri
Self::Iri => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Iri
))),
#[cfg(feature = "url")]
Self::IriReference => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::IriReference
))),
Self::Email => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Email
))),
Self::IdnEmail => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::IdnEmail
))),
Self::Hostname => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Hostname
))),
Self::IdnHostname => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::IdnHostname
))),
Self::Ipv4 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Ipv4
))),
Self::Ipv6 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Ipv6
))),
Self::UriTemplate => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::UriTemplate
))),
Self::JsonPointer => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::JsonPointer
))),
Self::RelativeJsonPointer => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::RelativeJsonPointer
))),
Self::Regex => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
utoipa::openapi::schema::KnownFormat::Regex
))),
Self::Custom(value) => tokens.extend(quote!(utoipa::openapi::SchemaFormat::Custom(
Self::Custom(value) => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Custom(
String::from(#value)
))),
Self::Unknown => (), // unknown we just skip it
Expand Down
13 changes: 13 additions & 0 deletions utoipa-gen/tests/schema_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3339,6 +3339,19 @@ fn derive_unnamed_struct_schema_type_override_with_format() {
}
}

#[test]
fn derive_unnamed_struct_schema_ipv4() {
let value = api_doc! {
#[schema(format = Ipv4)]
struct Ipv4(String);
};

assert_value! {value=>
"type" = r#""string""#, "Value type"
"format" = r#""ipv4""#, "Value format"
}
}

#[test]
fn derive_struct_override_type_with_object_type() {
let value = api_doc! {
Expand Down
Loading