Skip to content

Commit

Permalink
Add support for time crate
Browse files Browse the repository at this point in the history
Add support for time crate for `Component`s and `IntoParams` parameters.
This add support for `OffsetDateTime`, `Date`, `PrimitiveDateTime` and
`Duration` types and they are resolved as `String` by default.
`OffsetDateTime` and `PrimitiveDateTime` uses `date-time` format and
`Date` uses `date` format. `Duration` does not have default format.

Users wishing to use alternative representations for above `time` crate
types they need to override type with `#[component(value_type = ...)]`
property.
  • Loading branch information
juhaku committed Jul 24, 2022
1 parent eeb0dd4 commit 8e2b35d
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ chrono_with_format = ["utoipa-gen/chrono_with_format"]
decimal = ["utoipa-gen/decimal"]
yaml = ["serde_yaml"]
uuid = ["utoipa-gen/uuid"]
time = ["utoipa-gen/time"]

[dependencies]
serde = { version = "1.0", features = ["derive"] }
Expand All @@ -47,6 +48,7 @@ chrono = { version = "0.4", features = ["serde"] }
rust_decimal = "1"
rocket = "0.5.0-rc.1"
uuid = "1"
time = { version = "0.3.11", features = [ "serde-human-readable" ] }

[workspace]
members = [
Expand Down
45 changes: 45 additions & 0 deletions tests/component_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,51 @@ fn derive_component_with_chrono_with_chrono_feature() {
}
}

#[cfg(feature = "time")]
#[test]
fn derive_component_with_chrono_with_chrono_feature() {
use time::{Date, Duration, OffsetDateTime, PrimitiveDateTime};

let times = api_doc! {
struct Timetest {
datetime: OffsetDateTime,
primitive_date_time: PrimitiveDateTime,
date: Date,
duration: Duration,
}
};

assert_json_eq!(
&times,
json!({
"properties": {
"date": {
"format": "date",
"type": "string"
},
"datetime": {
"format": "date-time",
"type": "string"
},
"primitive_date_time": {
"format": "date-time",
"type": "string"
},
"duration": {
"type": "string"
}
},
"required": [
"datetime",
"primitive_date_time",
"date",
"duration"
],
"type": "object"
})
)
}

#[test]
fn derive_struct_component_field_type_override() {
let post = api_doc! {
Expand Down
1 change: 1 addition & 0 deletions utoipa-gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ decimal = []
rocket_extras = ["regex", "lazy_static"]
uuid = ["dep:uuid"]
axum_extras = []
time = []
33 changes: 30 additions & 3 deletions utoipa-gen/src/component_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ impl ComponentType<'_> {
feature = "chrono_with_format",
feature = "decimal",
feature = "rocket_extras",
feature = "uuid"
feature = "uuid",
feature = "time",
)))]
{
is_primitive(name)
Expand All @@ -32,6 +33,7 @@ impl ComponentType<'_> {
feature = "decimal",
feature = "rocket_extras",
feature = "uuid",
feature = "time",
))]
{
let mut primitive = is_primitive(name);
Expand All @@ -56,6 +58,14 @@ impl ComponentType<'_> {
primitive = matches!(name, "Uuid");
}

#[cfg(feature = "time")]
if !primitive {
primitive = matches!(
name,
"Date" | "PrimitiveDateTime" | "OffsetDateTime" | "Duration"
);
}

primitive
}
}
Expand Down Expand Up @@ -123,6 +133,12 @@ impl ToTokens for ComponentType<'_> {
"PathBuf" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }),
#[cfg(feature = "uuid")]
"Uuid" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }),
#[cfg(feature = "time")]
"Date" | "PrimitiveDateTime" | "OffsetDateTime" => {
tokens.extend(quote! { utoipa::openapi::ComponentType::String })
}
#[cfg(feature = "time")]
"Duration" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }),
_ => tokens.extend(quote! { utoipa::openapi::ComponentType::Object }),
}
}
Expand Down Expand Up @@ -180,12 +196,12 @@ impl Type<'_> {
};
let name = &*last_segment.ident.to_string();

#[cfg(not(any(feature = "chrono_with_format", feature = "uuid")))]
#[cfg(not(any(feature = "chrono_with_format", feature = "uuid", feature = "time")))]
{
is_known_format(name)
}

#[cfg(any(feature = "chrono_with_format", feature = "uuid"))]
#[cfg(any(feature = "chrono_with_format", feature = "uuid", feature = "time"))]
{
let mut known_format = is_known_format(name);

Expand All @@ -199,6 +215,11 @@ impl Type<'_> {
known_format = matches!(name, "Uuid");
}

#[cfg(feature = "time")]
if !known_format {
known_format = matches!(name, "Date" | "PrimitiveDateTime" | "OffsetDateTime");
}

known_format
}
}
Expand Down Expand Up @@ -231,6 +252,12 @@ impl ToTokens for Type<'_> {
"Date" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Date }),
#[cfg(feature = "uuid")]
"Uuid" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Uuid }),
#[cfg(feature = "time")]
"Date" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Date }),
#[cfg(feature = "time")]
"PrimitiveDateTime" | "OffsetDateTime" => {
tokens.extend(quote! { utoipa::openapi::ComponentFormat::DateTime })
}
_ => (),
}
}
Expand Down

0 comments on commit 8e2b35d

Please sign in to comment.